Skip to content

Latest commit

 

History

History
214 lines (181 loc) · 5.76 KB

i18n.md

File metadata and controls

214 lines (181 loc) · 5.76 KB

Internationalization (i18n)

This example shows you how to implement a central translation dictionary in Luigi Core.

Luigi Core provides a generic API for i18n. We will write a custom translation provider that gathers translation strings from static files hosted either on Luigi Core or on the micro-frontend side.

Steps

1. Create a custom translation function

Create a internationalization provider that will expose the getTranslation method for settings.customTranslationImplementation configuration.

File i18n-provider.js:

class I18nProvider {
  init() {
    // Could also be an Ajax based implementation.
    this.translationTable = {
      "en": {
        "COMMON": {
          "SELECT_ENVIRONMENT": "Select EN Environment ..."
        },
        "LABEL1": 'Label EN',
        "ENVIRONMENT_NUM": "Environment {num} EN"
      },
      "de": {
        "COMMON": {
          "SELECT_ENVIRONMENT": "Select DE Environment ..."
        },
        "LABEL1": "Label DE",
        "ENVIRONMENT_NUM": "Environment {num} DE"
      },
    };
    return Promise.resolve();
  }

  afterInit() {
    this.currentLanguage = Luigi.i18n().getCurrentLocale();
    Luigi.i18n().addCurrentLocaleChangeListener((locale) => {
      this.currentLanguage = locale;
    });
  }

  // This function will be used by Luigi Core for translation
  getTranslation(key, interpolations = undefined, locale = undefined) {
    if (!key) return '';
    this.currentLanguage = locale || this.currentLanguage || Luigi.i18n().getCurrentLocale();
    const result = this.findTranslation(
      key,
      this.translationTable[this.currentLanguage],
      interpolations
    );
    return result ? result : key;
  }

  /**
   * @private
   * Finds the translated value based on given key.
   * @param {string} key key to be translated
   * @param {*} obj translation table
   */
  findTranslation(key, obj, interpolations) {
    let splitted = key.split('.');
    for (let i = 0; i < splitted.length; i++) {
      let key = splitted[i];
      if (obj.hasOwnProperty(key) && typeof obj[key] === 'object') {
        obj = obj[key];
      } else {
        if (interpolations)
          return this.findInterpolations(obj[key], interpolations);
        return obj[key];
      }
    }
  }

  /**
   * @private
   * Replaces values that are defiend in translation strings
   * @param {string} key key to be translated
   * @param {*} interpolations translation table
   * @example
   * findInterpolations('Environment {num}', {num: 1})
   */
  findInterpolations(value, interpolations) {
    Object.keys(interpolations).forEach(item => {
      value = value.replace(
        new RegExp('{' + EscapingHelpers.escapeKeyForRegexp(item) + '}', 'gi'),
        interpolations[item]
      );
    });
    return value;
  }
}

export const i18nProvider = new I18nProvider();

2. Configure customTranslationImplementation

Specify settings.customTranslationImplementation:

import { i18nProvider } from './i18n-provider';
const coreConfig = {
  // ... partial setting ...
  settings: {
    customTranslationImplementation = i18nProvider
  }
}

3. Load translations before Luigi.setConfig()

Since translations may come from external sources, loaded asynchronously, you should load them ahead before Luigi.setConfig is triggered. Be aware that other Luigi Core API functionality is only available after initialization.

import { i18nProvider } from './i18n-provider';
const coreConfig = { /* the whole configuration */ }

i18nProvider.init().then(() => {
  Luigi.setConfig(coreConfig);
})

4. Add locale change listener after Luigi initialization

Specify customTranslationImplementation:

import { i18nProvider } from './i18n-provider';
const coreConfig = {
  // ... partial setting ...
  lifecycleHooks: {
    luigiAfterInit: () => {
      i18nProvider.afterInit();
    }
  }
}

5. Use translation as a node label

You can use the translation keys as node.label, on other labels in the configuration or programmatically.

const coreConfig = {
  navigation: {
    nodes: [{
      label: 'MF1.LABEL1',
      pathSegment: 'overview',
      // ...
    }],
    contextSwitcher: {
      defaultLabel: 'COMMON.SELECT_ENVIRONMENT',
      options: () => [1,2,3,4,5].map(num => ({
        label: Luigi.i18n().getTranslation('ENVIRONMENT_NUM', {num}),
        pathValue: 'env' + num
      }))
    },
  }
}

6. Change locale from Client side

Most of the time, the language selection will be done in a micro-frontend, like user profile. To be able to use LuigiClient.uxManager().setCurrentLocale function, we need to add locale change permissions on the node level.

const coreConfig = {
  navigation: {
    nodes: [{
      label: 'User Profile',
      pathSegment: 'profile',
      viewUrl: '/profile',
      clientPermissions: {
        changeCurrentLocale: true
      }
    }],

In the profile micro-frontend we can then set the desired language:

LuigiClient.uxManager().setCurrentLocale('en');

7. Read current language in the micro-frontends

To read the current active language, use LuigiClient.uxManager().getCurrentLocale. Like all state related functions, it should not be called before Luigi initialization.

function updateCurrentLanguage() {
  currentLanguage = LuigiClient.uxManager().getCurrentLocale();
}
LuigiClient.addInitListener(updateCurrentLanguage);
LuigiClient.addContextUpdateListener(updateCurrentLanguage);