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.
ZeroMQ pull-push pattern for Node JS
The article emphasizes ZeroMQ's flexibility for messaging in Node.js, highlighting the pull-push pattern ideal for high-volume distributed systems.
Daniel Gustaw
• 3 min read
Overload Signatures in Typescript
In TypeScript, we can specify a function that can be called in different ways by writing overload signatures. You can use this to define functions with returned type dependent from arguments values.
Daniel Gustaw
• 2 min read
CodinGame: Best fit to data - Rust - Regression Analysis
Linear and logarithmic regressions were derived here. Models were fitted in rust language. This article shows that sometimes it's worth improving the theoretical model before starting implementation.
Daniel Gustaw
• 6 min read