Skip to content

Commit

Permalink
feat(translate): fixed regarding reviewers notes
Browse files Browse the repository at this point in the history
  • Loading branch information
RomanKrasinskyi committed Feb 19, 2020
1 parent fccafe2 commit 0a1f43d
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 112 deletions.
115 changes: 27 additions & 88 deletions lib/bundle-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var Fs = require('fs');
var sizeOf = require('image-size');
var Path = require('path');
var Validator = require('jsonschema').Validator;
const ValidatorSchemaTranslations = require('./validator/schema-translations');

/**
* Run some validations to ensure that the platform will accept the theme
Expand Down Expand Up @@ -103,113 +104,51 @@ function validateThemeConfiguration(callback) {
}

/**
* Find translatable strings
* @param {object} schema
* @param {function} callback
*/
function getTranslatableStrings(schema, callback) {
var trackedKeys = ['name', 'content', 'label', 'settings', 'options'];

schema.forEach(element => {
Object.keys(element).forEach(key => {
const value = element[key];

if (trackedKeys.indexOf(key) === -1) {
return;
}

if (Array.isArray(value)) {
return getTranslatableStrings(value, callback);
}

callback(value);
});
});
}

/**
* Find i18n keys in schema
* @param {object} schema
* @return {array}
*/
function getI18nKeys(schema) {
var keys = [];

getTranslatableStrings(schema, (value) => {
if (
value
&& keys.indexOf(value) === -1
&& /^i18n\./.test(value)
) {
keys.push(value);
}
});

return keys;
}

/**
* Find missed i18n keys in schemaTranslations
* @param {array} keys
* @param {object} translations
* @return {array}
*/
function findMissedKeys(keys, translations) {
var translationsKeys = Object.keys(translations);
var missingKeys = [];

keys.forEach(key => {
if (translationsKeys.indexOf(key) === -1) {
missingKeys.push(key);
}
});

return missingKeys;
}

/**
* Ensure that schema translations exists and there are no missing keys.
* Ensure that schema translations exists and there are no missing or unused keys.
* @param {function} callback
* @return {function} callback
*/
function validateSchemaTranslations(callback) {
var v = new Validator();
var validatorTranslations = './schemaTranslations.schema.json';
var translations = {};
var keys = [];
var missedKeys;
var validation;
var errorMessage;
const validatorSchemaTranslations = new ValidatorSchemaTranslations();
const validator = new Validator();

if (this.themeConfig.schemaExists()) {
keys = getI18nKeys(this.themeConfig.getRawSchema());
validatorSchemaTranslations.setSchema(this.themeConfig.getRawSchema());
}

if (this.themeConfig.schemaTranslationsExists()) {
translations = this.themeConfig.getRawSchemaTranslations();
}

if (keys.length && _.isEmpty(translations)) {
errorMessage = 'Missed or corrupted schemaTranslations.json file'.red;

return callback(new Error(errorMessage));
try {
validatorSchemaTranslations.setTranslations(this.themeConfig.getRawSchemaTranslations());
} catch (e) {
throw new Error('Corrupted schemaTranslations.json file'.red);
}
} else if (validatorSchemaTranslations.getSchemaKeys().length) {
return callback(new Error('Missed schemaTranslations.json file'.red));
}

missedKeys = findMissedKeys(keys, translations);
validation = v.validate(translations, require(validatorTranslations));
const missedKeys = validatorSchemaTranslations.findMissedKeys();
const unusedKeys = validatorSchemaTranslations.findUnusedKeys();
const validation = validator.validate(
validatorSchemaTranslations.getTranslations(),
validatorSchemaTranslations.getValidationSchema(),
);

if ((validation.errors && validation.errors.length > 0) || (missedKeys && missedKeys.length > 0)) {
errorMessage = 'Your theme\'s schemaTranslations.json has errors:'.red;
if ((validation.errors && validation.errors.length) || missedKeys.length || unusedKeys.length) {
let errorMessage = 'Your theme\'s schemaTranslations.json has errors:';

missedKeys.forEach(key => {
errorMessage += '\r\nmissing translation key "'.red + key.red + '"'.red;
errorMessage += '\r\nmissing translation key "' + key + '"';
});

unusedKeys.forEach(key => {
errorMessage += '\r\nunused translation key "' + key + '"';
});

validation.errors.forEach(error => {
errorMessage += '\r\nschemaTranslations'.red + error.stack.substring(8).red;
errorMessage += '\r\nschemaTranslations' + error.stack.substring(8);
});

return callback(new Error(errorMessage));
return callback(new Error(errorMessage.red));
}

callback(null, true);
Expand Down
22 changes: 0 additions & 22 deletions lib/schemaTranslations.schema.json

This file was deleted.

2 changes: 1 addition & 1 deletion lib/theme-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ ThemeConfig.prototype.getSchemaTranslations = function(callback) {
var schemaTranslations;

try {
schemaTranslationsContent = Fs.readFileSync(this.schemaTranslationsPath, {encoding: 'utf-8'})
schemaTranslationsContent = Fs.readFileSync(this.schemaTranslationsPath, {encoding: 'utf-8'});
} catch (err) {
return callback(null, {});
}
Expand Down
112 changes: 112 additions & 0 deletions lib/validator/schema-translations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
class ValidatorSchemaTranslations {
constructor() {
this.trackedKeys = ['name', 'content', 'label', 'settings', 'options', 'group'];
this.validationSchema = require('./schemaTranslations.json');
this.translations = {};
this.schemaKeys = [];
this.translationsKeys = [];
}

/**
* Set schema.json
* @param {array} schema
*/
setSchema(schema) {
this.getTranslatableStrings(schema);
}

/**
* Set schemaTranslations.json
* @param {object} translations
*/
setTranslations(translations) {
this.translations = translations;

this.getTranslationsKeys();
}

/**
* Set i18n key from a schema into keys array
* @param {string} value
*/
setSchemaKeys(value) {
if (value && !this.schemaKeys.includes(value) && /^i18n\./.test(value)) {
this.schemaKeys.push(value);
}
}

/**
* Get validation schema
* @return {object}
*/
getValidationSchema() {
return this.validationSchema;
}

/**
* Get translations
* @return {array}
*/
getTranslations() {
return this.translations;
}

/**
* Get i18n keys from translations
* @return {array}
*/
getTranslationsKeys() {
this.translationsKeys = Object.keys(this.translations);
}

/**
* Get i18n keys from schema
* @return {array}
*/
getSchemaKeys() {
return this.schemaKeys;
}

/**
* Get translatable strings
* @param {array} schema
*/
getTranslatableStrings(schema) {
const context = this;

schema.forEach(element => {
Object.entries(element).forEach(function (item) {
const key = item[0];
const value = item[1];

if (!context.trackedKeys.includes(key)) {
return;
}

if (Array.isArray(value)) {
return context.getTranslatableStrings(value);
}

context.setSchemaKeys(value);
});
});
}

/**
* Find unused i18n keys in schemaTranslations
* @return {array}
*/
findUnusedKeys() {
return this.translationsKeys.filter(key => !this.schemaKeys.includes(key));
}

/**
* Find missed i18n keys in schemaTranslations
* @return {array}
*/
findMissedKeys() {
return this.schemaKeys.filter(key => !this.translationsKeys.includes(key));
}
}

module.exports = ValidatorSchemaTranslations;
67 changes: 67 additions & 0 deletions lib/validator/schema-translations.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
const Paths = require('path');
const Code = require('code');
const Lab = require('lab');
const lab = exports.lab = Lab.script();
const describe = lab.describe;
const it = lab.it;
const expect = Code.expect;

const ValidatorSchemaTranslations = require('./schema-translations');
const schema = require(Paths.join(process.cwd(), 'test/_mocks/themes/valid/schema.json'));
const schemaTranslations = require(Paths.join(process.cwd(), 'test/_mocks/themes/valid/schemaTranslations.json'));
const validationsTranslations = require('./schemaTranslations');

const validatorSchemaTranslations = () => {
return new ValidatorSchemaTranslations();
};

describe('ValidatorSchemaTranslations', () => {
it('should return translations', function (done) {
const instance = validatorSchemaTranslations();

instance.setTranslations(schemaTranslations);

expect(instance.getTranslations()).equals(schemaTranslations);

done();
});

it('should return translations keys', function (done) {
const instance = validatorSchemaTranslations();

instance.setSchema(schema);

expect(instance.getSchemaKeys()).equals(['i18n.Test']);

done();
});

it('should return validation schema', function (done) {
const instance = validatorSchemaTranslations();

expect(instance.getValidationSchema()).equals(validationsTranslations);

done();
});

it('should return i18n keys array without duplicates', function (done) {
const instance = validatorSchemaTranslations();

instance.setSchemaKeys('i18n.Global');
instance.setSchemaKeys('i18n.Global');

expect(instance.getSchemaKeys()).equals(['i18n.Global']);

done();
});

it('should return empty i18n keys array if specify empty string', function (done) {
const instance = validatorSchemaTranslations();

instance.setSchemaKeys('');

expect(instance.getSchemaKeys()).equals([]);

done();
});
});
29 changes: 29 additions & 0 deletions lib/validator/schemaTranslations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$id": "http://json-schema.org/draft-04/schema#",
"title": "Theme translations",
"description": "Translations of strings in schema.json file of a theme",
"type": "object",
"patternProperties": {
"^i18n.": {
"type": "object",
"properties": {
"default": {
"type": "string",
"minLength": 1
}
},
"patternProperties": {
"^[a-z]{2}$": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": false,
"required": [
"default"
]
}
},
"additionalProperties": false
}
2 changes: 1 addition & 1 deletion test/_mocks/themes/valid/schema.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"name": "Test",
"name": "i18n.Test",
"settings": [
{
"type": "heading",
Expand Down
Loading

0 comments on commit 0a1f43d

Please sign in to comment.