Internationalization

Kea - High level abstraction between React and Redux - Interview with Marius Andra

Internationalization (i18n) is a big topic by itself. The broadest definition has to do with translating your user interface to other languages. Localization (l10n) is a more specific term, and it describes how to adapt your application to a specific locale or market. Different locales can have the same language, but they still have their customs, like date formatting or measures.

The problem could be solved by pushing the translations behind an end point and loading them dynamically to decouple the problem from webpack. This would also allow you to implement a translation interface within your application to allow your translators, or even users, to translate the application. The downside of this approach is that then you have a translation backend to maintain.

Another approach is to let webpack generate static builds, each per language. The problem is that you have to update your application each time your translations change.

i18n with Webpack#

The basic idea of i18n with webpack is often the same. You have a translation definition that is then mapped to the application through replacements. The result contains a translated version of the application. You can use multiple translation formats through a couple of solutions:

To illustrate the setup, i18n-webpack-plugin is a good starting point.

Setting Up a Project#

To prove that translation works, set up something to replace:

app/i18n.js

console.log(__('Hello world'));

To translate that into Finnish, set up a definition:

languages/fi.json

{ "Hello world": "Terve maailma" }

To make ESLint aware of the global __ function, you should add it to your linting rules:

.eslintrc.js

module.exports = {
  ...
  globals: {
    __: true,
  },
  ...
};

The next step is to glue the files together using webpack.

Setting Up I18nWebpackPlugin#

Install i18n-webpack-plugin and glob helper first. The latter is needed for capturing translation files.

npm install glob i18n-webpack-plugin --save-dev

On the webpack side, you should iterate through the available languages, and then set up a configuration for each:

webpack.i18n.js

const path = require('path');
const glob = require('glob');
const I18nPlugin = require('i18n-webpack-plugin');

const PATHS = {
  build: path.join(__dirname, 'i18n-build'),
  i18nDemo: path.join(__dirname, 'app', 'i18n.js'),
};

const TRANSLATIONS = [{ language: 'en' }].concat(
  glob.sync('./languages/*.json').map((file) => ({
    language: path.basename(file, path.extname(file)),
    translation: require(file),
  }))
);

module.exports = TRANSLATIONS.map(({
  language, translation,
}) => (
  {
    entry: {
      index: PATHS.i18nDemo,
    },
    output: {
      path: PATHS.build,
      filename: `[name].${language}.js`,
    },
    plugins: [
      new I18nPlugin(translation),
    ],
  }
));

To make it convenient to build, set a shortcut:

package.json

"scripts": {
  "build:i18n": "webpack --config webpack.i18n.js",
  ...
},

If you build now (npm run build:i18n), you should end up with a new directory containing two translated files. If you examine them, you should see webpack bootstrap and the translated code in each.

To take the example further, you could generate a proper page for each translation as described in the Multiple Pages chapter. Language selector would be a good addition to the demonstration. You could handle the language definition through webpack's DefinePlugin. A user interface widget could rely on that and load another language based a page or directory naming convention.

The techniques discussed in the Code Splitting chapter are valid with i18n. You could define dynamic imports to load translation files on demand. Doing this would push the problem of loading and maintaining translations elsewhere.

Conclusion#

The other webpack approaches follow a similar idea but require more work on the frontend side. They are also more flexible, and if you go with a loader based solution, then you can set up split points to load languages on demand. You can also consider caching the language data to avoid fetching it altogether or loading it in smaller portions.

To recap:

  • Internationalization (i18n) and localization (l10n) are important problems if you target multiple markets with your application.
  • Webpack supports multiple approaches to i18n. The most basic approach is to replace specifically strings with other strings although more sophisticated alternatives are available.
  • The problem can be handled by pushing it to a server. This way would be a more dynamic and it would also allow you to handle translating the actual application through the same API.

The next chapter covers various testing setups and tools that work with webpack.

Previous chapterWeb Workers
Next chapterTesting

This book is available through Leanpub (digital), Amazon (paperback), and Kindle (digital). By purchasing the book you support the development of further content. A part of profit (~30%) goes to Tobias Koppers, the author of webpack.

Need help?