@joystick.js/ui

Internationalization (i18n)

How to configure and use translations in Joystick apps.

As your app grows, it's wise to internationalize your UI (offer translations for other languages). To help with this, Joystick supports built-in translation support for pages rendered via the res.render() method.

Storing translations

Translation files should be stored in the /i18n folder at the root of your app. Files should be named using a combination of the ISO-693 language code with the ISO-3166 country code (e.g., en-US.js, de-DE.js, es-ES.js, or ja-JP.js). This format matches the standard used by browsers.

An example /i18n folder might look like this:

/i18n folder structure

/i18n
-- de-DE.js
-- en-GB.js
-- en-US.js
-- es-ES.js
-- jp-JP.js

Above, we have translations for German, English (Great Britain), English (United States), Spanish, and Japanese.

Loading translations

When you render a page via res.render(), Joystick will try to find a matching language file based on the following preference order:

  1. The current user's (if available) language field value.
  2. The browser's navigator.language value.
  3. The config.i18n.default_language (alias: config.i18n.defaultLanguage) value.

If there's a matching language file in the /i18n folder, that file will be loaded automatically.

Matching based on page path

After a language match is found, inside of a language file, Joystick will attempt to match a value on the object exported by the language file matching the path of the page being rendered. For example, if we have a call to res.render() like this:

Server-side render call

res.render('ui/pages/profile/index.js');

Joystick will try to find something like this in the matching language file:

/i18n/en-US.js

const en_US = {
  'ui/pages/profile/index.js': {
    title: 'The Killer App the Internet Always Wanted'
  },
};

export default en_US;

If there's a match, Joystick will load only the translations stored in the value of that key, ignoring the rest of the translation file. If a matching path cannot be found, Joystick will fallback to loading the entire translation file.

Example Usage

Accessing translations in a component

Once a translation file is found, either the subset matching the page path or the entire translation file's contents are mapped over to the window.__joystick_i18n__ value. Internally, @joystick.js/ui looks for this value at mount time and loads it into the i18n value for all components in the current tree.

Inside of a component, then, we can do something like this:

/ui/pages/profile/index.js

import joystick from '@joystick.js/ui';

const Profile = joystick.component({
  css: { ... },
  render: ({ i18n }) => {
    return `
      <div class="page-profile">
        <div class="masthead">
          <h1>${i18n('title')}</h1>
        </div>
      </div>
    `;
  },
});

export default Profile;

In the example above, because our example language file had a matching page path for /ui/pages/profile/index.js, we expect the value of that key in the language file to be loaded in the browser. Using the i18n() render method, we pass the name of a key that we want to obtain the translation for relative to that file.

When the above component is rendered, we'd expect to get back some HTML like this:

Rendered HTML

<div class="page-profile">
  <div class="masthead">
    <h1>The Killer App the Internet Always Wanted</h1>
  </div>
</div>