grapesjs-clot
Version:
Free and Open Source Web Builder Framework
252 lines (220 loc) • 6.96 kB
JavaScript
/**
* You can customize the initial state of the module from the editor initialization, by passing the following [Configuration Object](https://github.com/artf/grapesjs/blob/master/src/i18n/config.js)
* ```js
* const editor = grapesjs.init({
* i18n: {
* locale: 'en',
* localeFallback: 'en',
* messages: {
* it: { hello: 'Ciao', ... },
* ...
* }
* }
* })
* ```
*
* Once the editor is instantiated you can use its API. Before using these methods you should get the module from the instance
*
* ```js
* const i18n = editor.I18n;
* ```
*
* ### Events
* * `i18n:add` - New set of messages is added
* * `i18n:update` - The set of messages is updated
* * `i18n:locale` - Locale changed
*
* @module I18n
*/
import { isUndefined, isString } from 'underscore';
import { hasWin } from 'utils/mixins';
import config from './config';
const isObj = el => !Array.isArray(el) && el !== null && typeof el === 'object';
const deepAssign = (...args) => {
const target = { ...args[0] };
for (let i = 1; i < args.length; i++) {
const source = { ...args[i] };
for (let key in source) {
const targValue = target[key];
const srcValue = source[key];
if (isObj(targValue) && isObj(srcValue)) {
target[key] = deepAssign(targValue, srcValue);
} else {
target[key] = srcValue;
}
}
}
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();
}
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);
}
return result;
},
_debug(str, opts = {}) {
const { em, config } = this;
(opts.debug || config.debug) && em && em.logWarning(str);
},
destroy() {
this.config = config;
this.em = {};
},
};
};