Source: i18n/modules/i18n.js

/**
 * @summary i18n
 * @version 3.0.0
 * @since 3.0.0
 * @author Arian Khosravi<arian.khosravi@aofl.com>
 */

/**
 * @memberof module:@aofl/i18n
 * @private
 * @type {RegExp}
 */
const REPLACE_REGEX = /%%?r(\d+)(?:%|::.*?%%)/g;
/**
 * @memberof module:@aofl/i18n
 * @private
 * @type {RegExp}
 */
const CONDITIONAL_REPLACE_REGEX = /%c(\d+)%/g;


/**
 * Provides translation capability.
 *
 * @memberof module:@aofl/i18n
 */
class I18n {
  /**
   * Creates an instance of I18n.
   *
   * @param {Object} [translations={}] - translations map
   */
  constructor(translations = {}, lang = 'en-US') {
    this.translations = translations;
    this.lang = lang;
  }

  static getDecorateOutput(id, str) {
    return window.aoflDevtools && window.aoflDevtools.showI18nIds? `<span id="${id}" class="aofl-i18n-string" title="${id}" style="position: relative;">${str}<span style="font-size: 12px; color: #000; position: absolute; left: 0; bottom: 0; transform: translateY(60%); white-space: pre; background: yellow; line-height: 1; padding: 0 2px; font-weight: 400;    z-index: 1;">${id.replace('<', '&lt;').replace('>', '&gt;')}</span></span>`: str;
  }
  /**
   * Lazy-load the translation map
   *
   * @param {String} lang
   * @return {Promise}
   */
  getTranslationMap(lang) {
    if (typeof this.translations !== 'undefined' && typeof this.translations[lang] === 'function') {
      return this.translations[lang]()
        .then((langModule) => langModule.default);
    }
    return Promise.resolve({});
  }

  /**
 * Language translation function.
 *
 * @param {String} id
 * @param {String} str
 * @return {String}
 */
  async __(id, str) {
    const languageMap = await this.getTranslationMap(this.lang);
    let out = str;

    if (typeof languageMap !== 'undefined' && typeof languageMap[id] === 'object' &&
      typeof languageMap[id].text === 'string') {
      out = languageMap[id].text;
    }

    return I18n.getDecorateOutput(id, out);
  }

  /**
   * Replace function. When invoked it will replace %r(number)% with the number matching the index
   * of the arguments passed to the _r function.
   *
   * @param {String} _str
   * @param {*} args
   * @return {String}
   */
  async _r(_str, ...args) {
    let str = await Promise.resolve(_str);
    /* istanbul ignore next */
    if (typeof str === 'object' && Array.isArray(str.strings) && str.strings.length) {
      str = str.strings[0];
    }

    let matches = REPLACE_REGEX.exec(str);
    let pivot = 0;
    let out = '';

    while (matches !== null) {
      const match = matches[0];
      const argIndex = matches[1] - 1;
      const offset = match.length;

      out += str.slice(pivot, matches.index) + args[argIndex];
      pivot = matches.index + offset;
      matches = REPLACE_REGEX.exec(str);
    }

    out += str.slice(pivot);

    return out;
  }

  /**
   * Conditional translation function. When invoked it finds the correct string based on the labels
   * specified in ...args.
   *
   * @param {*} id
   * @param {*} str
   * @param {*} args
   * @return {String}
   */
  async _c(id, str, ...args) {
    let out = '';
    const languageMap = await this.getTranslationMap(this.lang);

    /* istanbul ignore else */
    if (typeof languageMap !== 'undefined' && typeof languageMap === 'object') {
      const idParts = [];
      for (let i = 0; i < args.length; i = i + 2) {
        const nextI = i + 1;
        /* istanbul ignore next */
        if (nextI >= args.length) continue;
        let key = args[nextI];

        if (typeof args[i][key] === 'undefined') {
          key = '%other%';
        }

        idParts.push(key);
      }

      id += '-' + idParts.join('^^');
      if (typeof languageMap[id] === 'object' && typeof languageMap[id].text === 'string') {
        str = languageMap[id].text;
      }
    }

    // when language map is not matched we still need to replace %c#% in the default text
    let matches = CONDITIONAL_REPLACE_REGEX.exec(str);
    let pivot = 0;

    while (matches !== null) {
      const match = matches[0];
      const argIndex = (matches[1] - 1) * 2;
      let argCountIndex = args[argIndex + 1];
      const offset = match.length;

      if (typeof args[argIndex][argCountIndex] === 'undefined') {
        argCountIndex = '%other%';
      }

      out += str.slice(pivot, matches.index) + args[argIndex][argCountIndex];
      pivot = matches.index + offset;
      matches = CONDITIONAL_REPLACE_REGEX.exec(str);
    }

    out += str.slice(pivot);
    return I18n.getDecorateOutput(id, out);
  }
}

export {
  I18n
};