diff --git a/src/i18n/index.js b/src/i18n/index.js index 90efe1db3..777c6b995 100644 --- a/src/i18n/index.js +++ b/src/i18n/index.js @@ -27,8 +27,8 @@ * @module I18n */ import { isUndefined, isString } from 'underscore'; -import { hasWin } from '../utils/mixins'; -import config from './config'; +import { hasWin } from 'utils/mixins'; +import defaults from './config'; const isObj = el => !Array.isArray(el) && el !== null && typeof el === 'object'; @@ -53,199 +53,197 @@ const deepAssign = (...args) => { return target; }; -export default () => { - return { - name: 'I18n', - - config, - - /** - * Initialize module - * @param {Object} config Configurations - * @private - */ - init(opts = {}) { - this.config = { - ...config, - ...opts, - messages: { - ...config.messages, - ...(opts.messages || {}), - }, - }; - const add = this.config.messagesAdd; - add && this.addMessages(add); - - if (this.config.detectLocale) { - this.config.locale = this._localLang(); - } +export default class I18nModule { + name = 'I18n'; + + //config; + + /** + * Initialize module + * @param {Object} config Configurations + * @private + */ + init(opts = {}) { + this.config = { + ...defaults, + ...opts, + messages: { + ...defaults.messages, + ...(opts.messages || {}), + }, + }; + const add = this.config.messagesAdd; + add && this.addMessages(add); + + if (this.config.detectLocale) { + this.config.locale = this._localLang(); + } - this.em = opts.em; - return this; - }, - - /** - * Get module configurations - * @returns {Object} Configuration object - */ - getConfig() { - return this.config; - }, - - /** - * Update current locale - * @param {String} locale Locale value - * @returns {this} - * @example - * i18n.setLocale('it'); - */ - setLocale(locale) { - const { em, config } = this; - const evObj = { value: locale, valuePrev: config.locale }; - em && em.trigger('i18n:locale', evObj); - config.locale = locale; - return this; - }, - - /** - * Get current locale - * @returns {String} Current locale value - */ - getLocale() { - return this.config.locale; - }, - - /** - * Get all messages - * @param {String} [lang] Specify the language of messages to return - * @param {Object} [opts] Options - * @param {Boolean} [opts.debug] Show warnings in case of missing language - * @returns {Object} - * @example - * i18n.getMessages(); - * // -> { en: { hello: '...' }, ... } - * i18n.getMessages('en'); - * // -> { hello: '...' } - */ - getMessages(lang, opts = {}) { - const { messages } = this.config; - lang && !messages[lang] && this._debug(`'${lang}' i18n lang not found`, opts); - return lang ? messages[lang] : messages; - }, - - /** - * Set new set of messages - * @param {Object} msg Set of messages - * @returns {this} - * @example - * i18n.getMessages(); - * // -> { en: { msg1: 'Msg 1', msg2: 'Msg 2', } } - * i18n.setMessages({ en: { msg2: 'Msg 2 up', msg3: 'Msg 3', } }); - * // Set replaced - * i18n.getMessages(); - * // -> { en: { msg2: 'Msg 2 up', msg3: 'Msg 3', } } - */ - setMessages(msg) { - const { em, config } = this; - config.messages = msg; - em && em.trigger('i18n:update', msg); - return this; - }, - - /** - * Update messages - * @param {Object} msg Set of messages to add - * @returns {this} - * @example - * i18n.getMessages(); - * // -> { en: { msg1: 'Msg 1', msg2: 'Msg 2', } } - * i18n.addMessages({ en: { msg2: 'Msg 2 up', msg3: 'Msg 3', } }); - * // Set updated - * i18n.getMessages(); - * // -> { en: { msg1: 'Msg 1', msg2: 'Msg 2 up', msg3: 'Msg 3', } } - */ - addMessages(msg) { - const { em } = this; - const { messages } = this.config; - em && em.trigger('i18n:add', msg); - this.setMessages(deepAssign(messages, msg)); - - return this; - }, - - /** - * Translate the locale message - * @param {String} key Label to translate - * @param {Object} [opts] Options for the translation - * @param {Object} [opts.params] Params for the translation - * @param {Boolean} [opts.debug] Show warnings in case of missing resources - * @returns {String} - * @example - * obj.setMessages({ - * en: { msg: 'Msg', msg2: 'Msg {test}'}, - * it: { msg2: 'Msg {test} it'}, - * }); - * obj.t('msg'); - * // -> outputs `Msg` - * obj.t('msg2', { params: { test: 'hello' } }); // use params - * // -> outputs `Msg hello` - * obj.t('msg2', { l: 'it', params: { test: 'hello' } }); // custom local - * // -> outputs `Msg hello it` - */ - t(key, opts = {}) { - const { config } = this; - const param = opts.params || {}; - const locale = opts.l || this.getLocale(); - const localeFlb = opts.lFlb || config.localeFallback; - let result = this._getMsg(key, locale, opts); - - // Try with fallback - if (!result) result = this._getMsg(key, localeFlb, opts); - - !result && this._debug(`'${key}' i18n key not found in '${locale}' lang`, opts); - result = result && isString(result) ? this._addParams(result, param) : result; - - return result; - }, - - _localLang() { - const nav = (hasWin() && window.navigator) || {}; - const lang = nav.language || nav.userLanguage; - return lang ? lang.split('-')[0] : 'en'; - }, - - _addParams(str, params) { - const reg = new RegExp(`\{([\\w\\d-]*)\}`, 'g'); - return str.replace(reg, (m, val) => params[val] || '').trim(); - }, - - _getMsg(key, locale, opts = {}) { - const msgSet = this.getMessages(locale, opts); - - // Lang set is missing - if (!msgSet) return; - - let result = msgSet[key]; - - // Check for nested getter - if (!result && key.indexOf('.') > 0) { - result = key.split('.').reduce((lang, key) => { - if (isUndefined(lang)) return; - return lang[key]; - }, msgSet); - } + this.em = opts.em; + return this; + } - return result; - }, + /** + * Get module configurations + * @returns {Object} Configuration object + */ + getConfig() { + return this.config; + } - _debug(str, opts = {}) { - const { em, config } = this; - (opts.debug || config.debug) && em && em.logWarning(str); - }, + /** + * Update current locale + * @param {String} locale Locale value + * @returns {this} + * @example + * i18n.setLocale('it'); + */ + setLocale(locale) { + const { em, config } = this; + const evObj = { value: locale, valuePrev: config.locale }; + em && em.trigger('i18n:locale', evObj); + config.locale = locale; + return this; + } - destroy() { - this.config = config; - this.em = {}; - }, - }; -}; + /** + * Get current locale + * @returns {String} Current locale value + */ + getLocale() { + return this.config.locale; + } + + /** + * Get all messages + * @param {String} [lang] Specify the language of messages to return + * @param {Object} [opts] Options + * @param {Boolean} [opts.debug] Show warnings in case of missing language + * @returns {Object} + * @example + * i18n.getMessages(); + * // -> { en: { hello: '...' }, ... } + * i18n.getMessages('en'); + * // -> { hello: '...' } + */ + getMessages(lang, opts = {}) { + const { messages } = this.config; + lang && !messages[lang] && this._debug(`'${lang}' i18n lang not found`, opts); + return lang ? messages[lang] : messages; + } + + /** + * Set new set of messages + * @param {Object} msg Set of messages + * @returns {this} + * @example + * i18n.getMessages(); + * // -> { en: { msg1: 'Msg 1', msg2: 'Msg 2', } } + * i18n.setMessages({ en: { msg2: 'Msg 2 up', msg3: 'Msg 3', } }); + * // Set replaced + * i18n.getMessages(); + * // -> { en: { msg2: 'Msg 2 up', msg3: 'Msg 3', } } + */ + setMessages(msg) { + const { em, config } = this; + config.messages = msg; + em && em.trigger('i18n:update', msg); + return this; + } + + /** + * Update messages + * @param {Object} msg Set of messages to add + * @returns {this} + * @example + * i18n.getMessages(); + * // -> { en: { msg1: 'Msg 1', msg2: 'Msg 2', } } + * i18n.addMessages({ en: { msg2: 'Msg 2 up', msg3: 'Msg 3', } }); + * // Set updated + * i18n.getMessages(); + * // -> { en: { msg1: 'Msg 1', msg2: 'Msg 2 up', msg3: 'Msg 3', } } + */ + addMessages(msg) { + const { em } = this; + const { messages } = this.config; + em && em.trigger('i18n:add', msg); + this.setMessages(deepAssign(messages, msg)); + + return this; + } + + /** + * Translate the locale message + * @param {String} key Label to translate + * @param {Object} [opts] Options for the translation + * @param {Object} [opts.params] Params for the translation + * @param {Boolean} [opts.debug] Show warnings in case of missing resources + * @returns {String} + * @example + * obj.setMessages({ + * en: { msg: 'Msg', msg2: 'Msg {test}'}, + * it: { msg2: 'Msg {test} it'}, + * }); + * obj.t('msg'); + * // -> outputs `Msg` + * obj.t('msg2', { params: { test: 'hello' } }); // use params + * // -> outputs `Msg hello` + * obj.t('msg2', { l: 'it', params: { test: 'hello' } }); // custom local + * // -> outputs `Msg hello it` + */ + t(key, opts = {}) { + const { config } = this; + const param = opts.params || {}; + const locale = opts.l || this.getLocale(); + const localeFlb = opts.lFlb || config.localeFallback; + let result = this._getMsg(key, locale, opts); + + // Try with fallback + if (!result) result = this._getMsg(key, localeFlb, opts); + + !result && this._debug(`'${key}' i18n key not found in '${locale}' lang`, opts); + result = result && isString(result) ? this._addParams(result, param) : result; + + return result; + } + + _localLang() { + const nav = (hasWin() && window.navigator) || {}; + const lang = nav.language || nav.userLanguage; + return lang ? lang.split('-')[0] : 'en'; + } + + _addParams(str, params) { + const reg = new RegExp(`\{([\\w\\d-]*)\}`, 'g'); + return str.replace(reg, (m, val) => params[val] || '').trim(); + } + + _getMsg(key, locale, opts = {}) { + const msgSet = this.getMessages(locale, opts); + + // Lang set is missing + if (!msgSet) return; + + let result = msgSet[key]; + + // Check for nested getter + if (!result && key.indexOf('.') > 0) { + result = key.split('.').reduce((lang, key) => { + if (isUndefined(lang)) return; + return lang[key]; + }, msgSet); + } + + return result; + } + + _debug(str, opts = {}) { + const { em, config } = this; + (opts.debug || config.debug) && em && em.logWarning(str); + } + + destroy() { + this.config = {}; + this.em = {}; + } +} diff --git a/test/specs/i18n/index.js b/test/specs/i18n/index.js index 50357b1e4..20b93acbc 100644 --- a/test/specs/i18n/index.js +++ b/test/specs/i18n/index.js @@ -8,7 +8,7 @@ describe('I18n', () => { let em = editor.getModel(); beforeEach(() => { - obj = I18n(); + obj = new I18n(); obj.init({ em }); });