replaces mongoose-intl with local internationalization plugin
This commit is contained in:
200
src/plugins/internationalization.js
Normal file
200
src/plugins/internationalization.js
Normal file
@@ -0,0 +1,200 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
export default function internationalization(schema, options) {
|
||||
if (!options || !options.locales || !Array.isArray(options.locales) || !options.locales.length) {
|
||||
throw new mongoose.Error('Required locales array is missing');
|
||||
}
|
||||
|
||||
// plugin options to be set under schema options
|
||||
schema.options.mongooseIntl = {};
|
||||
const pluginOptions = schema.options.mongooseIntl;
|
||||
|
||||
pluginOptions.locales = options.locales.slice(0);
|
||||
|
||||
// the first available locale will be used as default if it's not set or unknown value passed
|
||||
if (!options.defaultLocale || pluginOptions.locales.indexOf(options.defaultLocale) === -1) {
|
||||
pluginOptions.defaultLocale = pluginOptions.locales[0];
|
||||
} else {
|
||||
pluginOptions.defaultLocale = options.defaultLocale.slice(0);
|
||||
}
|
||||
|
||||
schema.eachPath((path, schemaType) => {
|
||||
if (schemaType.schema) { // propagate plugin initialization for sub-documents schemas
|
||||
schemaType.schema.plugin(internationalization, pluginOptions);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!schemaType.options.intl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(schemaType instanceof mongoose.Schema.Types.String)) {
|
||||
throw new mongoose.Error('intl can only be used on type String');
|
||||
}
|
||||
|
||||
let pathArray = path.split('.'),
|
||||
key = pathArray.pop(),
|
||||
prefix = pathArray.join('.');
|
||||
|
||||
if (prefix) prefix += '.';
|
||||
|
||||
// removing real path, it will be changed to virtual later
|
||||
schema.remove(path);
|
||||
|
||||
// schema.remove removes path from paths object only, but doesn't update tree
|
||||
// sounds like a bug, removing item from the tree manually
|
||||
let tree = pathArray.reduce((mem, part) => {
|
||||
return mem[part];
|
||||
}, schema.tree);
|
||||
delete tree[key];
|
||||
|
||||
|
||||
schema.virtual(path)
|
||||
.get(function () {
|
||||
// embedded and sub-documents will use locale methods from the top level document
|
||||
let owner = this.ownerDocument ? this.ownerDocument() : this,
|
||||
locale = owner.getLocale(),
|
||||
localeSubDoc = this.getValue(path);
|
||||
|
||||
if (localeSubDoc === null || localeSubDoc === void 0) {
|
||||
return localeSubDoc;
|
||||
}
|
||||
|
||||
if (localeSubDoc.hasOwnProperty(locale)) {
|
||||
return localeSubDoc[locale];
|
||||
}
|
||||
|
||||
// are there any other locales defined?
|
||||
for (let prop in localeSubDoc) {
|
||||
if (localeSubDoc.hasOwnProperty(prop)) {
|
||||
// TODO: find a different lang to return
|
||||
return null; // some other locales exist, but the required is not - return null value
|
||||
}
|
||||
}
|
||||
return void 0; // no locales defined - the entire field is undefined
|
||||
})
|
||||
.set(function (value) {
|
||||
// multiple locales are set as an object
|
||||
if (typeof value === 'object') {
|
||||
let locales = this.schema.options.mongooseIntl.locales;
|
||||
locales.forEach(locale => {
|
||||
if (!value[locale]) {
|
||||
return;
|
||||
}
|
||||
this.set(path + '.' + locale, value[locale]);
|
||||
}, this);
|
||||
return;
|
||||
}
|
||||
|
||||
// embedded and sub-documents will use locale methods from the top level document
|
||||
let owner = this.ownerDocument ? this.ownerDocument() : this;
|
||||
this.set(path + '.' + owner.getLocale(), value);
|
||||
});
|
||||
|
||||
|
||||
// intl option is not needed for the current path any more,
|
||||
// and is unwanted for all child locale-properties
|
||||
delete schemaType.options.intl;
|
||||
|
||||
let intlObject = {};
|
||||
intlObject[key] = {};
|
||||
pluginOptions.locales.forEach(function (locale) {
|
||||
let localeOptions = Object.assign({}, schemaType.options);
|
||||
if (locale !== options.defaultLocale) {
|
||||
delete localeOptions.default;
|
||||
delete localeOptions.required;
|
||||
}
|
||||
|
||||
if (schemaType.options.defaultAll) {
|
||||
localeOptions.default = schemaType.options.defaultAll;
|
||||
}
|
||||
|
||||
if (schemaType.options.requiredAll) {
|
||||
localeOptions.required = schemaType.options.requiredAll;
|
||||
}
|
||||
// the use of this inside of a function caused an issue when changed to an arrow function
|
||||
// this object will not be scoped inside an arrow function but then I don't understand what is set here
|
||||
// if not lost at the end
|
||||
this[locale] = localeOptions;
|
||||
}, intlObject[key]);
|
||||
|
||||
// intlObject example:
|
||||
// { fieldName: {
|
||||
// en: '',
|
||||
// de: ''
|
||||
// }}
|
||||
schema.add(intlObject, prefix);
|
||||
});
|
||||
|
||||
// document methods to set the locale for each model instance (document)
|
||||
schema.method({
|
||||
getLocales: function () {
|
||||
return this.schema.options.mongooseIntl.locales;
|
||||
},
|
||||
getLocale: function () {
|
||||
return this.docLocale || this.schema.options.mongooseIntl.defaultLocale;
|
||||
},
|
||||
setLocale: function (locale) {
|
||||
if (locale && this.getLocales().indexOf(locale) !== -1) {
|
||||
this.docLocale = locale;
|
||||
}
|
||||
},
|
||||
unsetLocale: function () {
|
||||
delete this.docLocale;
|
||||
}
|
||||
});
|
||||
|
||||
// model methods to set the locale for the current schema
|
||||
schema.static({
|
||||
getLocales: function () {
|
||||
return this.schema.options.mongooseIntl.locales;
|
||||
},
|
||||
getDefaultLocale: function () {
|
||||
return this.schema.options.mongooseIntl.defaultLocale;
|
||||
},
|
||||
setDefaultLocale: function (locale) {
|
||||
|
||||
let updateLocale = function (schema, locale) {
|
||||
schema.options.mongooseIntl.defaultLocale = locale.slice(0);
|
||||
|
||||
// default locale change for sub-documents schemas
|
||||
schema.eachPath((path, schemaType) => {
|
||||
if (schemaType.schema) {
|
||||
updateLocale(schemaType.schema, locale);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
if (locale && this.getLocales().indexOf(locale) !== -1) {
|
||||
updateLocale(this.schema, locale);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Mongoose will emit 'init' event once the schema will be attached to the model
|
||||
schema.on('init', (model) => {
|
||||
// no actions are required in the global method is already defined
|
||||
if (model.db.setDefaultLocale) {
|
||||
return;
|
||||
}
|
||||
|
||||
// define a global method to change the locale for all models (and their schemas)
|
||||
// created for the current mongo connection
|
||||
model.db.setDefaultLocale = function (locale) {
|
||||
let model, modelName;
|
||||
for (modelName in this.models) {
|
||||
if (this.models.hasOwnProperty(modelName)) {
|
||||
model = this.models[modelName];
|
||||
model.setDefaultLocale && model.setDefaultLocale(locale);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// create an alias for the global change locale method attached to the default connection
|
||||
if (!mongoose.setDefaultLocale) {
|
||||
mongoose.setDefaultLocale = mongoose.connection.setDefaultLocale;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
const create = (req, res) => {
|
||||
req.model.setDefaultLanguage(req.locale);
|
||||
req.model.setDefaultLocale(req.locale);
|
||||
req.model.create(req.body, (err, result) => {
|
||||
if (err)
|
||||
return res.send(httpStatus.INTERNAL_SERVER_ERROR, { error: err });
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
const destroy = (req, res) => {
|
||||
req.model.setDefaultLanguage(req.locale);
|
||||
req.model.findOneAndDelete({ _id: req.params._id }, (err, doc) => {
|
||||
if (err)
|
||||
return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: err });
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
const findOne = (req, res) => {
|
||||
// req.model.setDefaultLanguage(req.locale);
|
||||
|
||||
req.model.findOne({ _id: req.params._id }, (err, doc) => {
|
||||
if (err)
|
||||
return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: err });
|
||||
@@ -11,7 +9,7 @@ const findOne = (req, res) => {
|
||||
return res.status(httpStatus.NOT_FOUND).send('Not Found');
|
||||
|
||||
if (req.locale) {
|
||||
doc.setLanguage(req.locale);
|
||||
doc.setLocale(req.locale);
|
||||
return res.json(doc.toJSON({ virtuals: true }));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import httpStatus from 'http-status';
|
||||
|
||||
const query = (req, res) => {
|
||||
if (req.query.locale) {
|
||||
req.model.setDefaultLanguage(req.query.locale);
|
||||
req.model.setDefaultLocale(req.query.locale);
|
||||
}
|
||||
|
||||
req.model.paginate(req.model.apiQuery(req.query), req.query, (err, result) => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
const update = (req, res) => {
|
||||
req.model.setDefaultLanguage(req.locale);
|
||||
req.model.findOne({ _id: req.params._id }, '', {}, (err, doc) => {
|
||||
if (!doc)
|
||||
return res.status(httpStatus.NOT_FOUND).send('Not Found');
|
||||
|
||||
@@ -52,8 +52,8 @@ describe('Payload Middleware', () => {
|
||||
body: {}
|
||||
};
|
||||
localization = {
|
||||
languages: ['en', 'es'],
|
||||
defaultLanguage: 'en'
|
||||
locales: ['en', 'es'],
|
||||
defaultLocale: 'en'
|
||||
};
|
||||
|
||||
});
|
||||
@@ -73,7 +73,7 @@ describe('Payload Middleware', () => {
|
||||
locale(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toEqual(localization.defaultLanguage);
|
||||
expect(req.locale).toEqual(localization.defaultLocale);
|
||||
});
|
||||
|
||||
it('Supports accept-language header', () => {
|
||||
@@ -92,7 +92,7 @@ describe('Payload Middleware', () => {
|
||||
locale(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toEqual(localization.defaultLanguage);
|
||||
expect(req.locale).toEqual(localization.defaultLocale);
|
||||
});
|
||||
|
||||
it('Query param takes precedence over header', () => {
|
||||
@@ -105,14 +105,14 @@ describe('Payload Middleware', () => {
|
||||
expect(req.locale).toEqual('es');
|
||||
});
|
||||
|
||||
it('Supports default language', () => {
|
||||
it('Supports default locale', () => {
|
||||
locale(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toEqual(localization.defaultLanguage);
|
||||
expect(req.locale).toEqual(localization.defaultLocale);
|
||||
});
|
||||
|
||||
it('Supports language all', () => {
|
||||
it('Supports locale all', () => {
|
||||
req.query.locale = '*';
|
||||
locale(localization)(req, res, next);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user