const _ = require('lodash') const dotize = require('dotize') const i18nMW = require('i18next-express-middleware') const i18next = require('i18next') const Promise = require('bluebird') const fs = require('fs-extra') const path = require('path') const yaml = require('js-yaml') /* global WIKI */ module.exports = { engine: null, namespaces: [], init() { this.namespaces = WIKI.data.localeNamespaces this.engine = i18next this.engine.init({ load: 'languageOnly', ns: this.namespaces, defaultNS: 'common', saveMissing: false, lng: WIKI.config.lang.code, fallbackLng: 'en' }) // Load current language + namespaces this.refreshNamespaces(true) // Listen for localization events WIKI.events.on('localization', (action) => { switch (action) { case 'reload': this.refreshNamespaces() break } }) return this }, /** * Attach i18n middleware for Express * * @param {Object} app Express Instance */ attachMiddleware (app) { app.use(i18nMW.handle(this.engine)) }, /** * Get all entries for a specific locale and namespace * * @param {String} locale Locale code * @param {String} namespace Namespace */ async getByNamespace(locale, namespace) { if (this.engine.hasResourceBundle(locale, namespace)) { let data = this.engine.getResourceBundle(locale, namespace) return _.map(dotize.convert(data), (value, key) => { return { key, value } }) } else { throw new Error('Invalid locale or namespace') } }, /** * Load entries from the DB for a single locale * * @param {String} locale Locale code * @param {*} opts Additional options */ async loadLocale(locale, opts = { silent: false }) { const res = await WIKI.models.locales.query().findOne('code', locale) if (res) { if (_.isPlainObject(res.strings)) { _.forOwn(res.strings, (data, ns) => { this.namespaces.push(ns) this.engine.addResourceBundle(locale, ns, data, true, true) }) } } else if (!opts.silent) { throw new Error('No such locale in local store.') } // -> Load dev locale files if present if (WIKI.IS_DEBUG) { try { const devEntriesRaw = await fs.readFileAsync(path.join(WIKI.SERVERPATH, `locales/${locale}.yml`), 'utf8') if (devEntriesRaw) { const devEntries = yaml.safeLoad(devEntriesRaw) _.forOwn(devEntries, (data, ns) => { this.namespaces.push(ns) this.engine.addResourceBundle(locale, ns, data, true, true) }) WIKI.logger.info(`Loaded dev locales from ${locale}.yml`) } } catch (err) { // ignore } } }, /** * Reload all namespaces for all active locales from the DB * * @param {Boolean} silent No error on fail */ async refreshNamespaces (silent = false) { await this.loadLocale(WIKI.config.lang.code, { silent }) if (WIKI.config.lang.namespacing) { for (let ns of WIKI.config.lang.namespaces) { await this.loadLocale(ns, { silent }) } } }, /** * Set the active locale * * @param {String} locale Locale code */ async setCurrentLocale(locale) { await Promise.fromCallback(cb => { return this.engine.changeLanguage(locale, cb) }) } }