Calculating the Difference Between JSON Files
Learn how to find missing translations in JSON files with dictionaries.
Daniel Gustaw
• 3 min read
In this article, we will demonstrate how to create a function that identifies the differences between two JSON files.
From an educational standpoint, this serves as an excellent example of using recursive functions. From a practical perspective, it’s a valuable tool for managing translations.
To begin, we’ll create a command that reads the files and prints all keys present in the first file but missing in the second file to the standard output.
We will start by checking if the files indicated as arguments exist:
const fs = require('fs')
const pathBase = `${process.cwd()}/${process.argv[2]}`;
const pathComp = `${process.cwd()}/${process.argv[3]}`;
if (!fs.existsSync(pathBase)) {
console.error(`File ${pathBase} not existst`);
process.exit()
}
if (!fs.existsSync(pathComp)) {
console.error(`File ${pathComp} not existst`);
process.exit()
}
Next, we’ll read the contents of these files and convert JSON to objects:
const base = JSON.parse(fs.readFileSync(pathBase).toString());
const comp = JSON.parse(fs.readFileSync(pathComp).toString());
Now, we’ll define a function for finding differences:
function getDiff(a, b) {
const res = {};
for (let key in a) {
if (a.hasOwnProperty(key)) {
if (!b.hasOwnProperty(key)) {
res[key] = a[key]
} else {
if (typeof a[key] === 'object') {
res[key] = getDiff(a[key], b[key])
}
}
if (res[key] && !Object.keys(res[key]).length) {
delete res[key];
}
}
}
return res;
}
This function takes a pair of objects and iterates through the keys of the first (base) object. If the second object (comparison) does not have the key, it is added to the result. If the key is present, it checks if the type is an object and, if so, recursively calls the getDiff function.
Finally, we delete keys with empty objects before displaying the results:
process.stdout.write(JSON.stringify(getDiff(base, comp)))
This program does not support arrays. For translation files, they aren’t necessary. If you want to read about more advanced methods for comparing JSON files, a good starting point is a thread on Stack Overflow:
Using jq or alternative command line tools to compare JSON files
Now, let’s see how the program works in practice with translation files. The first file, en_old.json, was prepared manually and covers all translations in the application, while the second file, en.json, was generated by i18next. The issue is that i18next did not detect all translations.
At first, I sorted both files manually using the service: codeshack.io/json-sorter
https://codeshack.io/json-sorter/
Next, I used diffchecker
to find the differences between them:
https://www.diffchecker.com/yffDMWff
Then I created a file with the missing translations:
node ../DevTools/json-diff.js src/locales/en_old.json src/locales/en.json > src/locales/en-codes.json
The file, displayed and formatted by jq, looks like this:
We can see that it includes all the missing keys.
When importing translation files, we can use the deepmerge package. The i18n configuration file might look like this:
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import deepmerge from 'deepmerge'
import en from 'vuetify/lib/locale/en'
import pl from 'vuetify/lib/locale/pl'
Vue.use(VueI18n);
const messages = {
en: deepmerge(
require('@/locales/en-codes.json'),
require('@/locales/en.json'),
{$vuetify: en}
),
pl: deepmerge(
require('@/locales/pl-codes.json'),
require('@/locales/pl.json'),
{$vuetify: pl}
),
};
export default new VueI18n({
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages,
})
export const languages = [
{text: 'lang.pl', value: 'pl'},
{text: 'lang.en', value: 'en'},
];
If you have any experiences related to automating translation work or recommendations for tools and scripts, feel free to share them in the comments. I’m interested in learning about the tools and approaches you use.
Other articles
You can find interesting also.
How many families can fit on the plane - an algorithmics problem
We compare two solutions to the problem of counting free sets of adjacent seats. You will learn how to use Profiling and how much difference the use of pop and shift makes on arrays in js.
Daniel Gustaw
• 12 min read
Broadcast Channel API
This post shows how to use the Broadcast Channel API to send data between browser tabs or windows without using a server and sockets.
Daniel Gustaw
• 11 min read
How the war for compatibility shaped the frontend?
We describe how deprecation and maintaining backward compatibility have influenced the direction of web technology development.
Daniel Gustaw
• 5 min read