"use strict"; // Copyright IBM Corp. 2018,2020. All Rights Reserved. // Node module: strong-globalize // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 Object.defineProperty(exports, "__esModule", { value: true }); exports.logPersistent = exports.setPersistentLogging = exports.consoleEnabled = exports.loadGlobalize = exports.formatCurrency = exports.formatDate = exports.formatNumber = exports.silly = exports.input = exports.verbose = exports.prompt = exports.data = exports.help = exports.log = exports.info = exports.warn = exports.debug = exports.informational = exports.notice = exports.warning = exports.error = exports.critical = exports.alert = exports.emergency = exports.rfc5424 = exports.packMessage = exports.formatJson = exports.formatMessage = exports.setAppLanguages = exports.setDefaultLanguage = exports.STRONGLOOP_GLB = exports.m = exports.t = exports.n = exports.d = exports.c = exports.setRootDir = void 0; //tslint:disable:no-any const assert = require("assert"); const debugModule = require("debug"); const fs = require("fs"); const Globalize = require("globalize"); const yamljs_1 = require("yamljs"); const config_1 = require("./config"); const helper = require("./helper"); const helper_1 = require("./helper"); const dbg = debugModule('strong-globalize'); const osLocale = require('os-locale'); const MapCache = require('lodash/_MapCache'); const md5 = require('md5'); const memoize = require('lodash/memoize'); const pathUtil = require('path'); const util = require('util'); var helper_2 = require("./helper"); Object.defineProperty(exports, "setRootDir", { enumerable: true, get: function () { return helper_2.setRootDir; } }); exports.c = formatCurrency; exports.d = formatDate; exports.n = formatNumber; exports.t = formatMessage; exports.m = formatMessage; var config_2 = require("./config"); Object.defineProperty(exports, "STRONGLOOP_GLB", { enumerable: true, get: function () { return config_2.STRONGLOOP_GLB; } }); /** * StrongLoop Defaults */ const SL_DEFAULT_DATETIME = { datetime: 'medium' }; const SL_DEFAULT_NUMBER = { round: 'floor' }; const SL_DEFAULT_CURRENCY = { style: 'name' }; const OS_LANG = osLanguage(); let MY_APP_LANG = process.env.STRONGLOOP_GLOBALIZE_APP_LANGUAGE; MY_APP_LANG = helper.isSupportedLanguage(MY_APP_LANG) ? MY_APP_LANG : null; function osLanguage() { const locale = osLocale.sync(); const lang = locale.substring(0, 2); if (helper.isSupportedLanguage(lang)) return lang; if (lang === 'zh') { const region = locale.substring(3); if (region === 'CN') return 'zh-Hans'; if (region === 'TW') return 'zh-Hant'; if (region === 'Hans') return 'zh-Hans'; if (region === 'Hant') return 'zh-Hant'; } } /** * setDefaultLanguage * * @param {string} (optional, default: `'en'`) Language ID. * It tries to use OS language, then falls back to 'en' */ function setDefaultLanguage(lang) { if (lang) lang = helper_1.getLangAlias(lang); lang = helper.isSupportedLanguage(lang) ? lang : undefined; lang = lang || MY_APP_LANG || OS_LANG || helper.ENGLISH; loadGlobalize(lang); if (lang !== helper.ENGLISH) { loadGlobalize(helper.ENGLISH); } config_1.STRONGLOOP_GLB.locale(lang); config_1.STRONGLOOP_GLB.DEFAULT_LANG = lang; return lang; } exports.setDefaultLanguage = setDefaultLanguage; /** * setAppLanguages * * @param {string} (optional, default: `[...]`) []. * Sets the supported languages for the application. * These should be a subset of the languages within the intl * directory. * * If no argument is passed, the function uses the contents of * the intl directory to determine the application languages. * */ function setAppLanguages(langs) { langs = langs || readAppLanguagesSync() || []; config_1.STRONGLOOP_GLB.APP_LANGS = langs; return langs; } exports.setAppLanguages = setAppLanguages; function readAppLanguagesSync() { try { const langs = fs.readdirSync(pathUtil.join(config_1.STRONGLOOP_GLB.MASTER_ROOT_DIR, 'intl')); return langs; } catch (ex) { return null; } } /** * Globalize.formatMessage wrapper returns a string. * * @param {string} path The message key * @param {object} variables List of placeholder key and content value pair. * @param {string} variables. The placeholder key. * @param {string} variables. The content value. * If the system locale is undefined, falls back to 'en' */ function formatMessage(path, variables, lang) { assert(path); if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG) setDefaultLanguage(); let message = path; if (helper.hashKeys(path)) { if (!config_1.STRONGLOOP_GLB.getHash) { config_1.STRONGLOOP_GLB.getHash = memoize(md5); } path = config_1.STRONGLOOP_GLB.getHash(path); } lang = lang || config_1.STRONGLOOP_GLB.DEFAULT_LANG; dbg('~~~ lang = %s %s %j %s', lang, path, variables, __filename); const trailer = helper.getTrailerAfterDot(path); if (trailer === 'json' || trailer === 'yml' || trailer === 'yaml') { const fullPath = pathUtil.join(helper.getRootDir(), path); return formatJson(fullPath, JSON.parse(variables), lang); } function formatMsgInline(language) { const g = config_1.STRONGLOOP_GLB.bundles[language]; if (!config_1.STRONGLOOP_GLB.formatters) { config_1.STRONGLOOP_GLB.formatters = new MapCache(); } const allFormatters = config_1.STRONGLOOP_GLB.formatters; let langFormatters; if (allFormatters.has(language)) { langFormatters = allFormatters.get(language); } else { langFormatters = new MapCache(); allFormatters.set(language, langFormatters); } if (langFormatters.has(path)) return langFormatters.get(path)(variables || {}); const format = g.messageFormatter(path); langFormatters.set(path, format); return format(variables || {}); } try { message = formatMsgInline(lang); } catch (e) { if (lang === helper.ENGLISH) { message = sanitizeMsg(message, variables); dbg('*** %s not found for %s. Fall back to: "%s" *** %s', path, lang, message, e); } else { dbg('*** %s for %s not localized. Fall back to English. *** %s', path, lang, e); try { message = formatMsgInline(helper.ENGLISH); } catch (e) { message = sanitizeMsg(message, variables); dbg('*** %s not found for %s. Fall back to: "%s" *** %s', path, lang, message, e); } } } if (config_1.STRONGLOOP_GLB.PSEUDO_LOC_PREAMBLE) { message = config_1.STRONGLOOP_GLB.PSEUDO_LOC_PREAMBLE + message; } return message; } exports.formatMessage = formatMessage; function formatJson(fullPath, variables, lang) { assert(fullPath === pathUtil.resolve(helper.getRootDir(), fullPath), '*** full path is required to format json/yaml file: ' + fullPath); const fileType = helper.getTrailerAfterDot(fullPath); let jsonData = null; try { const contentStr = fs.readFileSync(fullPath, 'utf8'); if (fileType === 'json') jsonData = JSON.parse(contentStr); if (fileType === 'yml' || fileType === 'yaml') jsonData = yamljs_1.parse(contentStr); } catch (_e) { return '*** read failure: ' + fullPath; } const msgs = helper.scanJson(variables, jsonData); const transMsgs = []; msgs.forEach(function (msg) { const transMsg = formatMessage(msg, undefined, lang); transMsgs.push(transMsg); }); helper.replaceJson(variables, jsonData, transMsgs); return jsonData; } exports.formatJson = formatJson; function sanitizeMsg(message, variables) { message = message.replace(/}}/g, '').replace(/{{/g, ''); if (typeof variables === 'string' || (Array.isArray(variables) && variables.length > 0)) { const sanitizedMsg = message.replace(/%[sdj]/g, '%s'); message = util.format.apply(util, [sanitizedMsg].concat(variables)); } return message; } function packMessage(args, fn, withOriginalMsg, lang) { const path = args[0]; const percentInKey = helper.percent(path); const txtWithTwoOrMoreArgs = helper.getTrailerAfterDot(path) === 'txt' && args.length > 2; // If it comes from *.txt, there are no percent in the path, // but there can be one or more %s in the message value. const variables = percentInKey ? helper.mapArgs(path, args) : txtWithTwoOrMoreArgs ? helper.repackArgs(args, 1) : args[1]; let message = formatMessage(path, variables, lang); if (withOriginalMsg) message = { language: lang, message: message, orig: path, vars: variables, }; if (fn) return fn(message); return message; } exports.packMessage = packMessage; /* RFC 5424 Syslog Message Severities * 0 Emergency: system is unusable * 1 Alert: action must be taken immediately * 2 Critical: critical conditions * 3 Error: error conditions * 4 Warning: warning conditions * 5 Notice: normal but significant condition * 6 Informational: informational messages * 7 Debug: debug-level messages */ function rfc5424(level, args, print, lang) { return packMessage(args, (msg) => { logPersistent(level, msg); if (consoleEnabled()) print(msg.message); return msg; }, true, lang); } exports.rfc5424 = rfc5424; function myError(...args) { const msg = packMessage(args, null, true); logPersistent('error', msg); return new Error(msg.message); } module.exports.Error = myError; // RFC 5424 Syslog Message Severities function emergency(...args) { return rfc5424('emergency', args, console.error); } exports.emergency = emergency; function alert(...args) { return rfc5424('alert', args, console.error); } exports.alert = alert; function critical(...args) { return rfc5424('critical', args, console.error); } exports.critical = critical; function error(...args) { return rfc5424('error', args, console.error); } exports.error = error; function warning(...args) { return rfc5424('warning', args, console.error); } exports.warning = warning; function notice(...args) { return rfc5424('notice', args, console.log); } exports.notice = notice; function informational(...args) { return rfc5424('informational', args, console.log); } exports.informational = informational; function debug(...args) { return rfc5424('debug', args, console.log); } exports.debug = debug; function warn(...args) { return rfc5424('warn', args, console.error); } exports.warn = warn; function info(...args) { return rfc5424('info', args, console.log); } exports.info = info; function log(...args) { return rfc5424('log', args, console.log); } exports.log = log; function help(...args) { return rfc5424('help', args, console.log); } exports.help = help; function data(...args) { return rfc5424('data', args, console.log); } exports.data = data; function prompt(...args) { return rfc5424('prompt', args, console.log); } exports.prompt = prompt; function verbose(...args) { return rfc5424('verbose', args, console.log); } exports.verbose = verbose; function input(...args) { return rfc5424('input', args, console.log); } exports.input = input; function silly(...args) { return rfc5424('silly', args, console.log); } exports.silly = silly; /** * Globalize.formatNumber wrapper returns a string. * * @param {value} integer or float * @param {object} The options (optional); if null, use the StrongLoop default. * Strongly recommended to set NO options and let strong-globalize use * the StrongLoop default for consistency across StrongLoop products. * See https://www.npmjs.com/package/globalize#number-module */ function formatNumber(value, options, lang) { assert(value); if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG) setDefaultLanguage(lang); lang = (lang || config_1.STRONGLOOP_GLB.DEFAULT_LANG); options = options || SL_DEFAULT_NUMBER; const G = config_1.STRONGLOOP_GLB.bundles[lang]; let msg = null; try { msg = G.formatNumber(value, options); } catch (e) { msg = value.toString(); dbg('*** formatNumber error: value:%s', msg); } return msg; } exports.formatNumber = formatNumber; /** * Globalize.formatDate wrapper returns a string. * * @param {Date object} such as new Date() * @param {object} The options (optional); if null, use the StrongLoop default. * Strongly recommended to set NO options and let strong-globalize use * the StrongLoop default for consistency across StrongLoop products. * See https://www.npmjs.com/package/globalize#date-module */ function formatDate(value, options, lang) { assert(value); if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG) setDefaultLanguage(lang); lang = (lang || config_1.STRONGLOOP_GLB.DEFAULT_LANG); options = options || SL_DEFAULT_DATETIME; const G = config_1.STRONGLOOP_GLB.bundles[lang]; let msg = null; try { msg = G.formatDate(value, options); } catch (e) { msg = value.toString(); dbg('*** formatDate error: value:%s', msg); } return msg; } exports.formatDate = formatDate; /** * Globalize.formatCurrency wrapper returns a string. * * @param {value} integer or float * @param {string} three-letter currency symbol, ISO 4217 Currency Code * @param {object} The options (optional); if null, use the StrongLoop default. * Strongly recommended to set NO options and let strong-globalize use * the StrongLoop default for consistency across StrongLoop products. * See https://www.npmjs.com/package/globalize#curency-module */ function formatCurrency(value, currencySymbol, options, lang) { assert(value && currencySymbol); if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG) setDefaultLanguage(lang); lang = lang || config_1.STRONGLOOP_GLB.DEFAULT_LANG; options = options || SL_DEFAULT_CURRENCY; const G = config_1.STRONGLOOP_GLB.bundles[lang]; let msg = null; try { msg = G.formatCurrency(value, currencySymbol, options); } catch (e) { msg = currencySymbol.toString() + value.toString(); dbg('*** formatCurrency error: value:%s, currencySymbol:%s', value, currencySymbol); } return msg; } exports.formatCurrency = formatCurrency; function loadGlobalize(lang, enumerateNodeModules) { assert(helper.isSupportedLanguage(lang), 'Not supported: ' + lang); if (!config_1.STRONGLOOP_GLB.versionSG) { const versionSG = helper.getPackageVersion(pathUtil.join(__dirname, '..')); const versionG = helper.getPackageVersion(pathUtil.join(__dirname, '..', 'node_modules', 'globalize')); Object.assign(config_1.STRONGLOOP_GLB, { versionSG: versionSG, versionG: versionG, bundles: {}, formatters: new MapCache(), getHash: memoize(md5), load: Globalize.load, locale: Globalize.locale, loadMessages: Globalize.loadMessages, DEFAULT_LANG: helper.ENGLISH, APP_LANGS: readAppLanguagesSync(), LOG_FN: null, DISABLE_CONSOLE: false, MASTER_ROOT_DIR: helper.getRootDir(), MSG_RES_LOADED: [], AUTO_MSG_LOADING: helper.AML_DEFAULT, PSEUDO_LOC_PREAMBLE: process.env.STRONG_GLOBALIZE_PSEUDO_LOC_PREAMBLE || '', }); loadCldr(helper.ENGLISH); config_1.STRONGLOOP_GLB.bundles[helper.ENGLISH] = new Globalize(helper.ENGLISH); config_1.STRONGLOOP_GLB.locale(helper.ENGLISH); helper.loadMsgFromFile(helper.ENGLISH, helper.getRootDir(), enumerateNodeModules); } if (!(lang in config_1.STRONGLOOP_GLB.bundles)) { loadCldr(lang); config_1.STRONGLOOP_GLB.bundles[lang] = new Globalize(lang); config_1.STRONGLOOP_GLB.locale(lang); helper.loadMsgFromFile(lang, helper.getRootDir(), enumerateNodeModules); } return config_1.STRONGLOOP_GLB.bundles[lang]; } exports.loadGlobalize = loadGlobalize; function loadCldr(lang) { assert(config_1.STRONGLOOP_GLB && (!config_1.STRONGLOOP_GLB.bundles || !config_1.STRONGLOOP_GLB.bundles[lang]), 'CLDR already loaded for ' + lang); const cldrDir = pathUtil.join(__dirname, '..', 'cldr'); helper.enumerateFilesSync(cldrDir, null, ['json'], false, false, function (content, filePath) { let cldr = null; try { cldr = JSON.parse(content); } catch (e) { throw new Error('*** CLDR read error on ' + process.platform); } const cldrMain = { main: {} }; cldrMain.main[lang] = cldr.main[lang]; config_1.STRONGLOOP_GLB.load(cldrMain); if (lang === helper.ENGLISH) { const cldrSupplemental = { supplemental: cldr.supplemental }; config_1.STRONGLOOP_GLB.load(cldrSupplemental); } }); } /** * * Persistent logging * */ // const syslogLevels = { // RFC5424 // emerg: 0, // alert: 1, // crit: 2, // error: 3, // warning: 4, // notice: 5, // info: 6, // debug: 7, // }; // const npmLevels = { // error: 0, // warn: 1, // info: 2, // verbose: 3, // debug: 4, // silly: 5, // }; function consoleEnabled() { if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG) setDefaultLanguage(); return !config_1.STRONGLOOP_GLB.DISABLE_CONSOLE; } exports.consoleEnabled = consoleEnabled; /** * */ function setPersistentLogging(logFn, disableConsole) { assert(logFn); assert(typeof logFn === 'function'); if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG) setDefaultLanguage(); config_1.STRONGLOOP_GLB.DISABLE_CONSOLE = !!disableConsole; try { const message = 'StrongGlobalize persistent logging started at ' + new Date(); logFn('info', { language: helper.ENGLISH, message: message, orig: message, vars: [], }); config_1.STRONGLOOP_GLB.LOG_FN = logFn; } catch (e) { config_1.STRONGLOOP_GLB.LOG_FN = undefined; } } exports.setPersistentLogging = setPersistentLogging; function logPersistent(level = 'info', message) { if (!config_1.STRONGLOOP_GLB.DEFAULT_LANG) setDefaultLanguage(); if (!config_1.STRONGLOOP_GLB.LOG_FN) return; level = level || 'info'; if (typeof config_1.STRONGLOOP_GLB.LOG_FN === 'function') { config_1.STRONGLOOP_GLB.LOG_FN(level, message); } } exports.logPersistent = logPersistent; //# sourceMappingURL=globalize.js.map