Source: index.js

import i18n from './i18n.js'
import { isArray, getUALang, objectAssign } from './utils.js'
import { getItem, setItem } from './store.js'

/**
 *  @class
 *  @name Switcher
 */
export default class Switcher {

  /**
   *  @typedef {function} getTransFile
   *  @param {object} lang
   *  @param {string} lang.systemLang - system language
   *  @param {string} lang.selectedLang - stored language
   *  @param {function} resolve
   *  @returns {string} format result
   *  @returns {string} selected language
   */
  /**
   *  @constructs Switcher
   *  @param {object} config
   *  @param {string} config.storageKey - key for persistent current language
   *  @param {object|function} config.translateText
   *  @param {object} config.formatters
   *  @param {getTransFile} config.translateFile - method for translate files
   *  @param {readyCallback} callback - fires when translation asserts are ready
   */
  constructor (config, callback) {

    this.formatters = config.formatters || {}
    this.storageKey = config.storageKey || 'locale'

    let currentLanguage = getItem(this.storageKey)
    let translateText = {}

    if (config.translateText) {
      if (typeof config.translateText === "function") {
        translateText = config.translateText({
          systemLang: getUALang(),
          selectedLang: currentLanguage
        })
      } else {
        translateText = config.translateText
      }
    }

    this.T = this.$T.bind(this)
    this.F = this.$F.bind(this)
    this.setLang = this.$L.bind(this)
    this.getLang = this.$G.bind(this)

    this.translateFile = config.translateFile

    this.getCallBack = function () { callback(this) }
    this.setLanguage(currentLanguage, translateText)
  }

  setLanguage (language, transPatch) {
    const resolve = (transAssert, currentLanguage) => {
      var translate = objectAssign({}, [transAssert, transPatch])
      this.currentLanguage = currentLanguage || language
      this.i18n = new i18n(translate)
      this.getCallBack()
    }

    this.currentLanguage = language
    if (this.translateFile) {
      this.translateFile({
        systemLang: getUALang(),
        selectedLang: language
      }, resolve)
    } else {
      resolve()
    }
  }
  /**
   *  Get current language
   *  @function getLang
   */
  $G () {
    return this.currentLanguage || getUALang() || ''
  }

  /**
   *  @typedef {function} fmtFunction
   *  @param {string|number} content - content to be formatted
   *  @param {string} lang - locale string
   *  @returns {string} format result
   */
  /**
   *  Register formatters
   *  @param {string} fmtName - formatter name
   *  @param {fmtFunction} func  - formatter function
   */
  register (fmtName, func) {
    if (typeof func !== 'function') {
      throw new TypeError('Formatter should be function, but got ' +
        typeof func)
    }
    this.formatters[fmtName] = func
  }

  /**
   *  Set current language
   *  @function L
   *  @param {string} lang - locale string
   *  @param {function} error  - call when setting failed
   *  @param {function} [success=reload] - call when setting success
   */
  $L (lang, error, success = () => { location.reload() }) {
    if (!setItem(this.storageKey, lang)) {
      if (typeof error === 'function') return error()
      return
    }
    this.currentLanguage = lang;
    if (typeof success === 'function') return success()
  }

  /**
   *  Translate given string
   *  @function T
   *  @param {string|string[]} item - string or array to be translated
   *  @param {object} interpolations - interpolations of placeholders in template
   *  @returns {string|string[]}
   */
  $T (item, interpolations) {
    if (typeof item === 'string') {
      return this.i18n.translate(item, interpolations)
    } else if (isArray(item)){
      return item.map(key => this.i18n.translate(key, interpolations))
    } else {
      throw new TypeError('Translate item should be string or array, but got ' +
        typeof item)
    }
  }

  /**
   *  Format given item
   *  @function F
   *  @param {string|number|string[]|number[]} item - string, number or array to be translated
   *  @param {string} fmtName - name of formatter
   *  @returns {string|string[]}
   */
  $F (fmtName, item, ...params) {
    let formatter = this.formatters[fmtName]
    if (!formatter) return console.error("Unregistered formatter:", fmtName)
    if (typeof item === 'string' || typeof item === 'number') {
      return formatter(item, this.currentLanguage, ...params)
    } else if (isArray(item)) {
      return item.map(str => formatter(str, this.currentLanguage, ...params))
    } else {
      throw new TypeError('Format item should be number, string or array, but got ' +
        typeof item)
    }
  }
}
/**
 * @callback readyCallback
 * @param {object} switcher - the created instance
 */