-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
110 lines (91 loc) · 3.03 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
'use strict';
const deepKeys = require('deep-keys');
const NO_DIFF = null;
/**
* Parse translations and return language key together with translations.
* @param {Object} o translations as values inside language key
* @returns {Array<String, Object>} Locale and translations
*/
function getTranslations(o) {
if (typeof o !== 'object') {
throw new TypeError(`Expected an object, got ${typeof o}`);
}
const locale = Object.keys(o)[0];
return [locale, o[locale]];
}
/**
* Returns distinct keys between both objects.
* @param {Object} a translations
* @param {Object} b translations
* @returns {Object} Differences ordered by language
*/
function diff(a, b) {
const [aLocale, aTranslations] = getTranslations(a);
const [bLocale, bTranslations] = getTranslations(b);
// Get nested keys of object in form of array
// Example:
// deepKeys({a:{b:{c:1}}}) returns ['a.b.c']
const deepA = deepKeys(aTranslations);
const deepB = deepKeys(bTranslations);
const missingInB = deepA.filter(x => deepB.indexOf(x) === -1);
const missingInA = deepB.filter(x => deepA.indexOf(x) === -1);
if (missingInA.length === 0 && missingInB.length === 0) {
return NO_DIFF;
}
return {[aLocale]: missingInB, [bLocale]: missingInA};
}
/**
* Returns differences between multiple objects
* @param {Array<Object>} objects Array of translations for each language
* supported
* @returns {Object} Differences ordered by language
*/
function diffs(...objects) {
let results = {};
objects.forEach(o1 => {
objects.forEach(o2 => {
const diffBetweenObjects = diff(o1, o2);
if (diffBetweenObjects !== NO_DIFF) {
results = {...results, ...diffBetweenObjects};
}
});
});
return results;
}
/**
* Transforms container of translations from object to array.
* @param {Object} translations Object with translations for each language
* @returns {Array<Object>} Array of translations ordered by language
* Example: [ { en: { form: 'Form' } }, { de: { form: 'Form' } } ]
*/
function formatTranslations(translations) {
const formattedTranslations = [];
Object.keys(translations).forEach(key => {
formattedTranslations.push({[key]: translations[key]});
});
return formattedTranslations;
}
/**
* Validates that no translations are missing.
* @param {Object} translations Object with translations for each language
* supported
* Example: {en: {save: 'Save'}, de: {save: 'Speichern'}}
* @returns {boolean} true if all translations contain the same keys
*/
function validi18n(translations) {
if (typeof translations !== 'object') {
throw new TypeError(`Expected an object, got ${typeof translations}`);
}
const formattedTranslations = formatTranslations(translations);
const missingTranslations = diffs(...formattedTranslations);
const ret = JSON.stringify(missingTranslations) === JSON.stringify({});
if (!ret) {
console.error(missingTranslations);
}
return ret;
}
module.exports = validi18n;
module.exports.diffs = diffs;
module.exports.diff = diff;
module.exports.formatTranslations = formatTranslations;
module.exports.getTranslations = getTranslations;