Skip to content

Instantly share code, notes, and snippets.

@aryomuzakki
Last active June 11, 2025 10:03
Show Gist options
  • Select an option

  • Save aryomuzakki/d5259dfbf7319c480e0dd999416f4dda to your computer and use it in GitHub Desktop.

Select an option

Save aryomuzakki/d5259dfbf7319c480e0dd999416f4dda to your computer and use it in GitHub Desktop.
Mirror keys in en/translation.json file into other languages.
/**
* Mirror keys in en/translation.json file into other languages.
* Will not overwrite, but non existing key will be removed.
*
* Add `ts-node` and `typescript` to devDependencies or global dependencies.
* Run with global package: `ts-node ./mirror-translation-file.ts`
* or
* Run with `bunx ts-node ./mirror-translation-file.ts`
* or
* Add to package.json scripts: `i18n-mirror: "bunx ts-node ./mirror-translation-file.ts"
* Run with `bun run i18n-mirror`
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-var-requires */
const fs = require('fs');
const path = require('path');
// optional declare language target or get target from existing config
const { languages } = require('./i18n-config');
// config
const SOURCE_LANG = 'en';
const DEFAULT_NS = 'translation';
const BASE_PATH = './src/i18n/locales';
const LANGUAGES: string[] = languages || ['id', 'th'];
// Paths to the source and target JSON files
const sourceJsonPath = `${BASE_PATH}/${SOURCE_LANG}/${DEFAULT_NS}.json`;
const targetJsonPath: string[] = LANGUAGES.filter(lang => lang != SOURCE_LANG);
// Function to ensure directories and files are created
const ensureDirectoryAndFile = (filePath: string) => {
const dirName = path.dirname(filePath);
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName, { recursive: true });
}
if (!fs.existsSync(filePath)) {
// Create an empty JSON file if it doesn't exist
fs.writeFileSync(filePath, JSON.stringify({}, null, 2));
}
};
// Reusable function to merge source and target JSON objects
const mergeObjects = (source: any, target: any): any => {
const result: any = {};
// A Set to track the keys of the source for faster lookup
const sourceKeys = Object.keys(source);
// Copy source keys into result in the same order
sourceKeys.forEach(key => {
if (typeof source[key] === 'object' && source[key] !== null) {
// Recursively merge nested objects
result[key] = mergeObjects(source[key], target[key] || {});
} else {
// Copy the key if it's missing in the target
if (!(key in target)) {
result[key] = source[key];
} else {
result[key] = target[key];
}
}
});
// Ensure that the target object only contains keys present in the source object
const targetKeys = Object.keys(target);
targetKeys.forEach(key => {
if (!sourceKeys.includes(key)) {
delete result[key];
}
});
return result;
};
// Function to merge source JSON into multiple destination JSONs
const mergeJsonFiles = (sourcePath: string, langs: string[], namespace: string = DEFAULT_NS) => {
console.log('!!! Non Existing key will be removed from destination files');
try {
// Read the source JSON file
const sourceJson = JSON.parse(fs.readFileSync(sourcePath, 'utf-8'));
// Iterate over the langs array for each destination language
langs.forEach(lang => {
// Generate target path for the current language
const targetPath = `${BASE_PATH}/${lang}/${namespace}.json`;
// Ensure the directory and file exist
ensureDirectoryAndFile(targetPath);
// Read the existing target JSON file
const targetJson = JSON.parse(fs.readFileSync(targetPath, 'utf-8'));
// Perform the merge operation
const mergedJson = mergeObjects(sourceJson, targetJson);
// Write the merged content back to the target JSON file
const updatedJsonString = JSON.stringify(mergedJson, null, 2);
fs.writeFileSync(targetPath, updatedJsonString, 'utf-8');
console.log(`Successfully updated target JSON: ${targetPath}`);
});
} catch (error: any) {
console.error(`Error: ${error.message}`);
}
};
// Call the function to merge JSON files
mergeJsonFiles(sourceJsonPath, targetJsonPath);
// output:
// !!! Non Existing key will be removed from destination files
// Successfully updated target JSON: ./src/i18n/locales/id/translation.json
// Successfully updated target JSON: ./src/i18n/locales/th/translation.json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment