You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
3.1 KiB

  1. const _ = require('lodash')
  2. const dotize = require('dotize')
  3. const i18nMW = require('i18next-express-middleware')
  4. const i18next = require('i18next')
  5. const Promise = require('bluebird')
  6. const fs = require('fs-extra')
  7. const path = require('path')
  8. const yaml = require('js-yaml')
  9. /* global WIKI */
  10. module.exports = {
  11. engine: null,
  12. namespaces: [],
  13. init() {
  14. this.namespaces = WIKI.data.localeNamespaces
  15. this.engine = i18next
  16. this.engine.init({
  17. load: 'languageOnly',
  18. ns: this.namespaces,
  19. defaultNS: 'common',
  20. saveMissing: false,
  21. lng: WIKI.config.lang.code,
  22. fallbackLng: 'en'
  23. })
  24. // Load current language + namespaces
  25. this.refreshNamespaces(true)
  26. return this
  27. },
  28. /**
  29. * Attach i18n middleware for Express
  30. *
  31. * @param {Object} app Express Instance
  32. */
  33. attachMiddleware (app) {
  34. app.use(i18nMW.handle(this.engine))
  35. },
  36. /**
  37. * Get all entries for a specific locale and namespace
  38. *
  39. * @param {String} locale Locale code
  40. * @param {String} namespace Namespace
  41. */
  42. async getByNamespace(locale, namespace) {
  43. if (this.engine.hasResourceBundle(locale, namespace)) {
  44. let data = this.engine.getResourceBundle(locale, namespace)
  45. return _.map(dotize.convert(data), (value, key) => {
  46. return {
  47. key,
  48. value
  49. }
  50. })
  51. } else {
  52. throw new Error('Invalid locale or namespace')
  53. }
  54. },
  55. /**
  56. * Load entries from the DB for a single locale
  57. *
  58. * @param {String} locale Locale code
  59. * @param {*} opts Additional options
  60. */
  61. async loadLocale(locale, opts = { silent: false }) {
  62. const res = await WIKI.models.locales.query().findOne('code', locale)
  63. if (res) {
  64. if (_.isPlainObject(res.strings)) {
  65. _.forOwn(res.strings, (data, ns) => {
  66. this.namespaces.push(ns)
  67. this.engine.addResourceBundle(locale, ns, data, true, true)
  68. })
  69. }
  70. } else if (!opts.silent) {
  71. throw new Error('No such locale in local store.')
  72. }
  73. // -> Load dev locale files if present
  74. if (WIKI.IS_DEBUG) {
  75. try {
  76. const devEntriesRaw = await fs.readFile(path.join(WIKI.SERVERPATH, `locales/${locale}.yml`), 'utf8')
  77. if (devEntriesRaw) {
  78. const devEntries = yaml.safeLoad(devEntriesRaw)
  79. _.forOwn(devEntries, (data, ns) => {
  80. this.namespaces.push(ns)
  81. this.engine.addResourceBundle(locale, ns, data, true, true)
  82. })
  83. WIKI.logger.info(`Loaded dev locales from ${locale}.yml`)
  84. }
  85. } catch (err) {
  86. // ignore
  87. }
  88. }
  89. },
  90. /**
  91. * Reload all namespaces for all active locales from the DB
  92. *
  93. * @param {Boolean} silent No error on fail
  94. */
  95. async refreshNamespaces (silent = false) {
  96. await this.loadLocale(WIKI.config.lang.code, { silent })
  97. if (WIKI.config.lang.namespacing) {
  98. for (let ns of WIKI.config.lang.namespaces) {
  99. await this.loadLocale(ns, { silent })
  100. }
  101. }
  102. },
  103. /**
  104. * Set the active locale
  105. *
  106. * @param {String} locale Locale code
  107. */
  108. async setCurrentLocale(locale) {
  109. await Promise.fromCallback(cb => {
  110. return this.engine.changeLanguage(locale, cb)
  111. })
  112. }
  113. }