2021-01-05 14:50:59 +00:00
|
|
|
const path = require('path');
|
2021-02-25 20:02:35 +00:00
|
|
|
const { availableLangs } = require('../../config/i18n/all-langs');
|
2021-02-18 05:08:45 +00:00
|
|
|
const introSchema = require('./locales/english/intro.json');
|
2021-03-13 00:14:54 +00:00
|
|
|
const linksSchema = require('./locales/english/links.json');
|
2021-08-02 13:39:40 +00:00
|
|
|
const metaTagsSchema = require('./locales/english/meta-tags.json');
|
|
|
|
const motivationSchema = require('./locales/english/motivation.json');
|
|
|
|
const translationsSchema = require('./locales/english/translations.json');
|
|
|
|
const trendingSchema = require('./locales/english/trending.json');
|
2021-01-05 14:50:59 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Flattens a nested object structure into a single
|
|
|
|
* object with property chains as keys.
|
|
|
|
* @param {Object} obj Object to flatten
|
|
|
|
* @param {String} namespace Used for property chaining
|
|
|
|
*/
|
|
|
|
const flattenAnObject = (obj, namespace = '') => {
|
|
|
|
const flattened = {};
|
|
|
|
Object.keys(obj).forEach(key => {
|
|
|
|
if (Array.isArray(obj[key])) {
|
|
|
|
flattened[namespace ? `${namespace}.${key}` : key] = obj[key];
|
|
|
|
} else if (typeof obj[key] === 'object') {
|
|
|
|
Object.assign(
|
|
|
|
flattened,
|
|
|
|
flattenAnObject(obj[key], namespace ? `${namespace}.${key}` : key)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
flattened[namespace ? `${namespace}.${key}` : key] = obj[key];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return flattened;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if a translation object is missing keys
|
|
|
|
* that are present in the schema.
|
|
|
|
* @param {String[]} file Array of translation object's keys
|
|
|
|
* @param {String[]} schema Array of matching schema's keys
|
|
|
|
* @param {String} path string path to file
|
|
|
|
*/
|
|
|
|
const findMissingKeys = (file, schema, path) => {
|
|
|
|
const missingKeys = [];
|
|
|
|
for (const key of schema) {
|
|
|
|
if (!file.includes(key)) {
|
|
|
|
missingKeys.push(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (missingKeys.length) {
|
2021-02-28 18:35:21 +00:00
|
|
|
console.warn(
|
2021-01-05 14:50:59 +00:00
|
|
|
`${path} is missing these required keys: ${missingKeys.join(', ')}`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if a translation object has extra
|
|
|
|
* keys which are NOT present in the schema.
|
|
|
|
* @param {String[]} file Array of translation object's keys
|
|
|
|
* @param {String[]} schema Array of matching schema's keys
|
|
|
|
* @param {String} path string path to file
|
|
|
|
*/
|
|
|
|
const findExtraneousKeys = (file, schema, path) => {
|
|
|
|
const extraKeys = [];
|
|
|
|
for (const key of file) {
|
|
|
|
if (!schema.includes(key)) {
|
|
|
|
extraKeys.push(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (extraKeys.length) {
|
2021-02-28 18:35:21 +00:00
|
|
|
console.warn(
|
2021-01-05 14:50:59 +00:00
|
|
|
`${path} has these keys that are not in the schema: ${extraKeys.join(
|
|
|
|
', '
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validates that all values in the object are non-empty. Includes
|
|
|
|
* validation of nested objects.
|
|
|
|
* @param {Object} obj The object to check the values of
|
|
|
|
* @param {String} namespace String for tracking nested properties
|
|
|
|
*/
|
|
|
|
const noEmptyObjectValues = (obj, namespace = '') => {
|
|
|
|
const emptyKeys = [];
|
|
|
|
for (const key of Object.keys(obj)) {
|
|
|
|
if (Array.isArray(obj[key])) {
|
|
|
|
if (!obj[key].length) {
|
|
|
|
emptyKeys.push(namespace ? `${namespace}.${key}` : key);
|
|
|
|
}
|
|
|
|
} else if (typeof obj[key] === 'object') {
|
|
|
|
emptyKeys.push(
|
|
|
|
noEmptyObjectValues(obj[key], namespace ? `${namespace}.${key}` : key)
|
|
|
|
);
|
2021-02-11 12:40:32 +00:00
|
|
|
} else if (!obj[key]) {
|
2021-01-05 14:50:59 +00:00
|
|
|
emptyKeys.push(namespace ? `${namespace}.${key}` : key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return emptyKeys.flat();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Grab the schema keys once, to avoid overhead of
|
|
|
|
* fetching within iterative function.
|
|
|
|
*/
|
|
|
|
const translationSchemaKeys = Object.keys(flattenAnObject(translationsSchema));
|
|
|
|
const trendingSchemaKeys = Object.keys(flattenAnObject(trendingSchema));
|
|
|
|
const motivationSchemaKeys = Object.keys(flattenAnObject(motivationSchema));
|
2021-01-13 13:09:45 +00:00
|
|
|
const introSchemaKeys = Object.keys(flattenAnObject(introSchema));
|
2021-02-03 08:03:52 +00:00
|
|
|
const metaTagsSchemaKeys = Object.keys(flattenAnObject(metaTagsSchema));
|
2021-03-13 00:14:54 +00:00
|
|
|
const linksSchemaKeys = Object.keys(flattenAnObject(linksSchema));
|
2021-01-05 14:50:59 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Function that checks the translations.json file
|
|
|
|
* for each available client language.
|
|
|
|
* @param {String[]} languages List of languages to test
|
|
|
|
*/
|
|
|
|
const translationSchemaValidation = languages => {
|
|
|
|
languages.forEach(language => {
|
|
|
|
const filePath = path.join(
|
|
|
|
__dirname,
|
|
|
|
`/locales/${language}/translations.json`
|
|
|
|
);
|
2021-02-18 05:08:45 +00:00
|
|
|
const fileJson = require(filePath);
|
2021-01-05 14:50:59 +00:00
|
|
|
const fileKeys = Object.keys(flattenAnObject(fileJson));
|
|
|
|
findMissingKeys(
|
|
|
|
fileKeys,
|
|
|
|
translationSchemaKeys,
|
|
|
|
`${language}/translations.json`
|
|
|
|
);
|
|
|
|
findExtraneousKeys(
|
|
|
|
fileKeys,
|
|
|
|
translationSchemaKeys,
|
|
|
|
`${language}/translations.json`
|
|
|
|
);
|
|
|
|
const emptyKeys = noEmptyObjectValues(fileJson);
|
|
|
|
if (emptyKeys.length) {
|
2021-02-28 18:35:21 +00:00
|
|
|
console.warn(
|
2022-06-27 13:44:56 +00:00
|
|
|
`${language}/translations.json has these empty keys: ${emptyKeys.join(
|
2021-01-05 14:50:59 +00:00
|
|
|
', '
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
}
|
2022-06-27 13:44:56 +00:00
|
|
|
console.info(`${language} translations.json validation complete.`);
|
2021-01-05 14:50:59 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function that checks the trending.json file
|
|
|
|
* for each available client language.
|
|
|
|
* @param {String[]} languages List of languages to test
|
|
|
|
*/
|
|
|
|
const trendingSchemaValidation = languages => {
|
|
|
|
languages.forEach(language => {
|
|
|
|
const filePath = path.join(__dirname, `/locales/${language}/trending.json`);
|
2021-02-18 05:08:45 +00:00
|
|
|
const fileJson = require(filePath);
|
2021-01-05 14:50:59 +00:00
|
|
|
const fileKeys = Object.keys(flattenAnObject(fileJson));
|
|
|
|
findMissingKeys(fileKeys, trendingSchemaKeys, `${language}/trending.json`);
|
|
|
|
findExtraneousKeys(
|
|
|
|
fileKeys,
|
|
|
|
trendingSchemaKeys,
|
|
|
|
`${language}/trending.json`
|
|
|
|
);
|
|
|
|
const emptyKeys = noEmptyObjectValues(fileJson);
|
|
|
|
if (emptyKeys.length) {
|
2021-02-28 18:35:21 +00:00
|
|
|
console.warn(
|
2021-01-05 14:50:59 +00:00
|
|
|
`${language}/trending.json has these empty keys: ${emptyKeys.join(
|
|
|
|
', '
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
}
|
2021-02-28 18:35:21 +00:00
|
|
|
console.info(`${language} trending.json validation complete`);
|
2021-01-05 14:50:59 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const motivationSchemaValidation = languages => {
|
|
|
|
languages.forEach(language => {
|
|
|
|
const filePath = path.join(
|
|
|
|
__dirname,
|
|
|
|
`/locales/${language}/motivation.json`
|
|
|
|
);
|
2021-02-18 05:08:45 +00:00
|
|
|
const fileJson = require(filePath);
|
2021-01-05 14:50:59 +00:00
|
|
|
const fileKeys = Object.keys(flattenAnObject(fileJson));
|
|
|
|
findMissingKeys(
|
|
|
|
fileKeys,
|
|
|
|
motivationSchemaKeys,
|
|
|
|
`${language}/motivation.json`
|
|
|
|
);
|
|
|
|
findExtraneousKeys(
|
|
|
|
fileKeys,
|
|
|
|
motivationSchemaKeys,
|
|
|
|
`${language}/motivation.json`
|
|
|
|
);
|
|
|
|
const emptyKeys = noEmptyObjectValues(fileJson);
|
|
|
|
if (emptyKeys.length) {
|
2021-02-28 18:35:21 +00:00
|
|
|
console.warn(
|
2021-01-05 14:50:59 +00:00
|
|
|
`${language}/motivation.json has these empty keys: ${emptyKeys.join(
|
|
|
|
', '
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// Special line to assert that objects in motivational quote are correct
|
|
|
|
if (
|
|
|
|
!fileJson.motivationalQuotes.every(
|
|
|
|
object =>
|
2021-12-02 00:01:12 +00:00
|
|
|
Object.prototype.hasOwnProperty.call(object, 'quote') &&
|
|
|
|
Object.prototype.hasOwnProperty.call(object, 'author')
|
2021-01-05 14:50:59 +00:00
|
|
|
)
|
|
|
|
) {
|
2021-02-28 18:35:21 +00:00
|
|
|
console.warn(`${language}/motivation.json has malformed quote objects.`);
|
2021-01-05 14:50:59 +00:00
|
|
|
}
|
2021-02-28 18:35:21 +00:00
|
|
|
console.info(`${language} motivation.json validation complete`);
|
2021-01-05 14:50:59 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2021-01-13 13:09:45 +00:00
|
|
|
/**
|
|
|
|
* Function that checks the intro.json file
|
|
|
|
* for each available client language.
|
|
|
|
* @param {String[]} languages List of languages to test
|
|
|
|
*/
|
|
|
|
const introSchemaValidation = languages => {
|
|
|
|
languages.forEach(language => {
|
|
|
|
const filePath = path.join(__dirname, `/locales/${language}/intro.json`);
|
2021-02-18 05:08:45 +00:00
|
|
|
const fileJson = require(filePath);
|
2021-01-13 13:09:45 +00:00
|
|
|
const fileKeys = Object.keys(flattenAnObject(fileJson));
|
2021-04-15 04:01:18 +00:00
|
|
|
findMissingKeys(fileKeys, introSchemaKeys, `${language}/intro.json`);
|
|
|
|
findExtraneousKeys(fileKeys, introSchemaKeys, `${language}/intro.json`);
|
2021-01-13 13:09:45 +00:00
|
|
|
const emptyKeys = noEmptyObjectValues(fileJson);
|
|
|
|
if (emptyKeys.length) {
|
2021-02-28 18:35:21 +00:00
|
|
|
console.warn(
|
2021-01-13 13:09:45 +00:00
|
|
|
`${language}/intro.json has these empty keys: ${emptyKeys.join(', ')}`
|
|
|
|
);
|
|
|
|
}
|
2021-02-28 18:35:21 +00:00
|
|
|
console.info(`${language} intro.json validation complete`);
|
2021-01-13 13:09:45 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2021-02-03 08:03:52 +00:00
|
|
|
const metaTagsSchemaValidation = languages => {
|
|
|
|
languages.forEach(language => {
|
|
|
|
const filePath = path.join(
|
|
|
|
__dirname,
|
|
|
|
`/locales/${language}/meta-tags.json`
|
|
|
|
);
|
2021-02-18 05:08:45 +00:00
|
|
|
const fileJson = require(filePath);
|
2021-02-03 08:03:52 +00:00
|
|
|
const fileKeys = Object.keys(flattenAnObject(fileJson));
|
|
|
|
findMissingKeys(fileKeys, metaTagsSchemaKeys, `${language}/meta-tags.json`);
|
|
|
|
findExtraneousKeys(
|
|
|
|
fileKeys,
|
|
|
|
metaTagsSchemaKeys,
|
|
|
|
`${language}/metaTags.json`
|
|
|
|
);
|
|
|
|
const emptyKeys = noEmptyObjectValues(fileJson);
|
|
|
|
if (emptyKeys.length) {
|
2021-02-28 18:35:21 +00:00
|
|
|
console.warn(
|
2021-02-03 08:03:52 +00:00
|
|
|
`${language}/metaTags.json has these empty keys: ${emptyKeys.join(
|
|
|
|
', '
|
|
|
|
)}`
|
|
|
|
);
|
|
|
|
}
|
2021-02-28 18:35:21 +00:00
|
|
|
console.info(`${language} metaTags.json validation complete`);
|
2021-02-03 08:03:52 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2021-03-13 00:14:54 +00:00
|
|
|
const linksSchemaValidation = languages => {
|
|
|
|
languages.forEach(language => {
|
|
|
|
const filePath = path.join(__dirname, `/locales/${language}/links.json`);
|
|
|
|
const fileJson = require(filePath);
|
|
|
|
const fileKeys = Object.keys(flattenAnObject(fileJson));
|
2021-04-15 04:01:18 +00:00
|
|
|
findMissingKeys(fileKeys, linksSchemaKeys, `${language}/links.json`);
|
|
|
|
findExtraneousKeys(fileKeys, linksSchemaKeys, `${language}/links.json`);
|
2021-03-13 00:14:54 +00:00
|
|
|
const emptyKeys = noEmptyObjectValues(fileJson);
|
|
|
|
if (emptyKeys.length) {
|
|
|
|
console.warn(
|
|
|
|
`${language}/links.json has these empty keys: ${emptyKeys.join(', ')}`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
console.info(`${language} links.json validation complete`);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2021-02-18 05:08:45 +00:00
|
|
|
const translatedLangs = availableLangs.client.filter(x => x !== 'english');
|
|
|
|
|
|
|
|
translationSchemaValidation(translatedLangs);
|
|
|
|
trendingSchemaValidation(translatedLangs);
|
|
|
|
motivationSchemaValidation(translatedLangs);
|
|
|
|
introSchemaValidation(translatedLangs);
|
|
|
|
metaTagsSchemaValidation(translatedLangs);
|
2021-03-13 00:14:54 +00:00
|
|
|
linksSchemaValidation(translatedLangs);
|