Internationalization

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 particular 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 endpoint and loading them dynamically to decouple the issue from webpack. Doing this would also allow you to implement a translation interface within your application to enable 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" }

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

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

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 and translated code in each.

To take the example further, generate a page for each translation as described in the Multiple Pages chapter and add a language selector. The language definition can be handled through webpack's DefinePlugin. A user interface widget could rely on that and load languages based on 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 and are more flexible but require more work. If you go with a loader based solution, then you can set up split points to load languages on demand.

To recap:

  • Internationalization (i18n) and localization (l10n) are important problems if you target multiple markets with your application.
  • Webpack supports multiple approaches to i18n. As a starting point you can replace specific annotations although more sophisticated alternatives are available.
  • The problem can be handled by pushing it to a server. 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.