Dynamic i18n in React 17

Adrian Celczyński
3 min readMay 31, 2021


Recently I have been challenged with adding localization to our flagship product @tdsoft.com

The task was to add localization to our content, date strings, and UI Elements.

Content is handled by copywriters so nothing more than a different API call was needed to handle that. But what about the other two?

Photo by Sigmund on Unsplash

My goto question to myself with every new feature we add to this project is:

Can this be lazy-loaded?

I have started my research and firstly https://github.com/i18next/react-i18next has popped up in search results. There are two main ways to do localization with this package:

  • Send localizable strings through the network by your backend
  • Bundle it 😏

I like to work with well-established packages, maintained by open source contributors but I thought I will try myself with the implementation hopefully keeping the app’s bundle smaller by doing so.

here is how it went for me 🤲

Dynamic import of Locale files for date strings 📆

We use date-fns for date formatting. Implementation is quite straightforward:

  • Dynamic import from node_modules/date-fns/Locale directory — let webpack handle everything. Nice thing to do is to add webpackChunkName with i18n prefix so it’s not only numbers that show in the network tab.
  • MobX store that triggers the import on mount

Mobx store is used to handle imports and makes imported values observable. Then with a simple react hook we serve observed values to our components.

Webpack knows what to import based on files kept in directories we import from. With webpackChunkName prefix, we set some logical structure in the built output.

Voila, works like a charm.

Dynamic import of UI Localizable Strings 🔠

I thought it would be efficient to keep default en-GB translations inside the components, so they can serve also as the keys of localizable strings. We import localizable strings on mount, but it is fast enough that they are available before the first paint.

The same trick as with date locale is introduced here. Mobx store handles imports.

That’s not every use case we had to cover. Sport-event dynamic strings like outcomes of the match (example: Manchester United to win nil) that include team names can be translated with team names in a different order. For this purpose, I have introduced the concept of double curly-braced templates, so translators can put variables in the order they prefer.


Feeding components with translations — useLocalizable hook 🪝

as you can see, there is PrePlayFootballLocalizableStrings interface being passed to the hook. With this assertion, we get the full auto-completion support from IDE, and type safety (as long as we import the right interface for the scope of the translations 😋)

here is the hook itself:

Some numbers ⚖️

i18next packages (not including translations):

  • i18next — 33kb minified
  • i18next react — 18.9kB minified

Our implementation (not including translations or packages we already use in the project: mobx, mobx-state-tree)

  • useLocalizible hook — 9KB minified
  • locale store — 9KB minified

I’m pretty satisfied with the results given the deadline from the client. It’s definitely something worth improving further.