Skip to content

This is a POC repo for exploring micro-frontend integration using Webpack 5 Module Federation & react-i18next. It is composed of a main React 17 app and a React 17 MFE which gets consumed.

Notifications You must be signed in to change notification settings

giavinh79/react-microfrontend-internationalization

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 

Repository files navigation

React Microfrontend & Webpack 5 Module Federation i18n Setup

Description

This is a simple POC repo for setting up a "main" React 17 app (react-main-container) which consumes a React 17 microfrontend (react-mfe) using Webpack 5 module federation. The main purpose of this POC is to investigate internationalization (react-i18next) with this type of setup for the best compromise between colocated MFE translations, TypeScript support, and ideal testing.

For a history of different approaches and tradeoffs, see commit history. Ultimately the current state of the repo represents an architecture where MFEs own their own i18n instances for more flexibility and independence (rather than pass down the i18n instance from the host to the MFE to be shared). The MFE also is able to remain in sync with the host app instance on language changes and can also access some of its translations if necessary (at the cost of less typesafety for those translations and having to mock those translations in tests).

Running

  1. Run the microfrontend (runs on localhost:5000)
  • cd react-mfe
  • yarn
  • yarn start
  • Since react-mfe leverages the i18n instance from react-main-container, it cannot run independently. Solving this is not within the scope of this POC.
  1. Run the main app (runs on localhost:4000)
  • cd react-main-container
  • yarn
  • yarn start
  1. Go to localhost:4000 & click on the button to toggle between English & French locales.

Linting

  1. yarn lint
  2. yarn lint:i18n for ensuring translation files in different locales for that app have identical keys, are sorted alphabetically, and follow a proper format.

Overview

  1. The main application, react-main-container, defines in webpack.config.jswhat microfrontends it will consume.
  2. The MFE, react-mfe, exposes Microfrontend.tsx in webpack.config.js so that it can be imported by react-main-container.
  3. react-main-container initializes two namespaces: common and app
  4. react-mfe initializes three namespaces: mfe, error, and host-common (a namespace loaded from host into MFE)

Reference

Sharing assets between MFE & Host and more:

Typescript Integration

In order to typically (integrate TypeScript)[https://react.i18next.com/latest/typescript] for react-i18next internationalization projects, a i18next.d.ts file is added which imports project locale files for type safety and augmented intellisense. Now, when using the t translate function, you'll get errors on invalid keys & see available keys! One caveat though, is when using the useTranslation hook you must pass in the namespaces being used otherwise the types will not work as expected.

This works fine for typical monolithic frontend applications however, in an architecture where an app consumes multiple i18n instances, TypeScript is a lot more difficult to configure. Based on comments in the repo, there does not appear to be official support for this type of situation - www.github.com/i18next/react-i18next/issues/726#issuecomment-1499882853.

As mentioned above although typescript integration is straightforward in the host app since there is only one i18n instance, the MFE app accesses both its own translations and the host app ones. When configuring typescript for the MFE i18n instance with i18next.d.ts, due to its global namespace, it also affects types for the host app instance (and when we import certain APIs like the Trans component, it'll automatically use types generated by the MFE i18next.d.ts making it difficult to use for host app translations). I can make it work by typing the host app instance with an overridden t field (i.e. typed as any) so it won't error when using host translation keys but you do lose intellisense. Alternatively, using types like TFunction gives it more intellisense but also makes it more annoying to work with for the Trans component - overall it's very hacky playing with the types like this.

Since the goal of this POC is to also assume that MFEs are in their own repo, it's difficult to enable full type safety for host app translations (i.e. if it was a monorepo for example, it could have access to directly import those types/translation files). There are some tools/approaches like https://github.com/module-federation/universe/tree/main/packages/typescript, npm packages, and https://spin.atomicobject.com/2022/07/19/typescript-federated-modules/ for sharing assets across federated modules but this does add complexity and isn't guaranteed to work due to how TypeScript works with i18-next (where you augment types in a single .d.ts file) + us using multiple i18n instances. This requires us to maybe rethink our approach of having multiple i18n instances.

Assuming we want the MFE to have access to host app translations, there are mainly two approaches. The first approach assumes you want to keep separate i18n instances (the host app i18n instance is passed to MFE and consumed with React Context) in which case it may be better to forego typescript integration for the microfrontend to avoid running into edge cases and annoyances with typing host app translations. Alternatively, we have a single i18n instance in the MFE that dynamically loads in host translations and then we manually type namespaces for the host translations. For simplicity we can type the namespace so that for that namespace any strings can be accepted (so it doesn't have full type safety but errors if strings aren't passed in the t function for example) - this is done in the current POC. Alternatively, since host translations get dynamically loaded into the MFE instance at run-time, the only way to have full type safety for host translations is for the MFE to have a "synced copy" of the host app locale files whether this is through an npm package or some process to pull in those files from the host app copy.

Testing

Assuming (react-testing-library)[https://testing-library.com/docs/react-testing-library/setup/] and Jest is being used, some relevant docs are:

Ultimately there are two different approaches we can go with for setting up our tests to work with i18next. We can either choose to:

  1. Mock i18n and related functionalities
  1. Do not mock i18n and translate text in tests
  • Assertions on text will look like: screen.getByText('Welcome to the application!') or screen.getByText(ENCommonTranslations.GREETING) (if you directly import the JSON translations file)

This project uses option 2 as it gives more confidence that things are working (the translation key actually has an associated translation & i18n is set up correctly).In addition, where possible, importing the JSON translations file to use in assertions keeps things concise, is synced to the translation so if you update the translation the test still works fine, and you can easily ctrl-click to go directly to that translation (nice DX). However for more complex translations with interpolation (i.e. "Welcome {{ name }} to the app") you cannot rely on just importing this translation from the JSON file in your test and will need to explicitly write out the string.

About

This is a POC repo for exploring micro-frontend integration using Webpack 5 Module Federation & react-i18next. It is composed of a main React 17 app and a React 17 MFE which gets consumed.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published