From fe3b85e22a1d5a8f9dd00b9f4b92b5508d6190b2 Mon Sep 17 00:00:00 2001 From: Alex Ritter Date: Wed, 13 Apr 2022 16:27:49 +0200 Subject: [PATCH] Convert i18n to class --- src/i18n/index.js | 386 +++++++++++++++++++-------------------- test/specs/i18n/index.js | 52 +++--- 2 files changed, 217 insertions(+), 221 deletions(-) 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 87e3e3816..70fbf0ea3 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 }); }); @@ -34,8 +34,8 @@ describe('I18n', () => { localeFallback, detectLocale: 0, messages: { - en: { msg } - } + en: { msg }, + }, }); expect(obj.getLocale()).toBe(locale); expect(obj.getConfig().localeFallback).toBe(localeFallback); @@ -44,7 +44,7 @@ describe('I18n', () => { test('English always imported', () => { obj.init({ - messages: { it: {} } + messages: { it: {} }, }); expect(Object.keys(obj.getMessages())).toEqual(['en', 'it']); }); @@ -74,12 +74,12 @@ describe('I18n', () => { obj.setMessages(set1); const set2 = { en: { msg2: 'Msg 2 up', msg3: 'Msg 3' }, - it: { msg1: 'Msg 1' } + it: { msg1: 'Msg 1' }, }; obj.addMessages(set2); expect(obj.getMessages()).toEqual({ en: { msg1: 'Msg 1', msg2: 'Msg 2 up', msg3: 'Msg 3' }, - it: { msg1: 'Msg 1' } + it: { msg1: 'Msg 1' }, }); }); @@ -90,19 +90,19 @@ describe('I18n', () => { msg2: 'Msg 2', msg3: { msg31: 'Msg 31', - msg32: { msg321: 'Msg 321' } - } - } + msg32: { msg321: 'Msg 321' }, + }, + }, }); obj.addMessages({ en: { msg2: { msg21: 'Msg 21' }, msg3: { msg32: { msg322: 'Msg 322' }, - msg33: 'Msg 33' + msg33: 'Msg 33', }, - msg4: 'Msg 4' - } + msg4: 'Msg 4', + }, }); expect(obj.getMessages()).toEqual({ en: { @@ -112,12 +112,12 @@ describe('I18n', () => { msg31: 'Msg 31', msg32: { msg321: 'Msg 321', - msg322: 'Msg 322' + msg322: 'Msg 322', }, - msg33: 'Msg 33' + msg33: 'Msg 33', }, - msg4: 'Msg 4' - } + msg4: 'Msg 4', + }, }); }); @@ -126,7 +126,7 @@ describe('I18n', () => { obj.setLocale('en'); obj.setMessages({ en: { msg1 }, - it: { msg1: `${msg1} it` } + it: { msg1: `${msg1} it` }, }); expect(obj.t('msg2')).toBe(undefined); expect(obj.t('msg1')).toBe(msg1); @@ -141,10 +141,10 @@ describe('I18n', () => { key1: { msg1, key2: { - msg2 - } - } - } + msg2, + }, + }, + }, }); expect(obj.t('key1.msg1')).toBe(msg1); expect(obj.t('key1.key2.msg2')).toBe(msg2); @@ -158,7 +158,7 @@ describe('I18n', () => { obj.setLocale('en'); obj.setMessages({ en: { msg1 }, - it: { msg1: msg1Alt } + it: { msg1: msg1Alt }, }); expect(obj.t('msg1', { l: 'it' })).toBe(msg1Alt); }); @@ -168,7 +168,7 @@ describe('I18n', () => { obj.setLocale('it'); obj.setMessages({ en: { msg1 }, - it: {} + it: {}, }); expect(obj.t('msg1')).toBe(msg1); }); @@ -179,12 +179,10 @@ describe('I18n', () => { obj.setLocale('en'); obj.setMessages({ en: { msg1 }, - it: { msg1: msg1Alt } + it: { msg1: msg1Alt }, }); expect(obj.t('msg1', { params: { test: 'Hello' } })).toBe('Msg 1 Hello'); - expect(obj.t('msg1', { l: 'it', params: { test: 'Hello' } })).toBe( - 'Msg 1 Hello it' - ); + expect(obj.t('msg1', { l: 'it', params: { test: 'Hello' } })).toBe('Msg 1 Hello it'); }); test('i18n events', () => {