This code analyzes translation keys to identify misplaced, unused, and missing keys, helping ensure consistency and completeness across different language versions of a project.
npm run import -- "Find unused/misplaced translation strings"var path = require('path');
var fs = require('fs');
var cwd = '/Users/briancullinan/Documents/portal/src/';
//var cwd = 'C:\\Users\\brian.cullinan\\Documents\\portal\\src\\';
function findMisplaced(obj, parentKey) {
for (var k in obj) {
if (typeof obj[k] == 'object' && obj[k] !== null) {
var newParent = typeof parentKey !== 'undefined' ? (parentKey + '.' + k) : k;
findMisplaced(obj[k], newParent);
} else if (typeof obj[k] == 'string') {
if (obj[k].substr(0, parentKey.length) != parentKey) {
console.log('Misplaced key: ' + obj[k] + ' in ' + parentKey);
}
}
}
}
findMisplaced(translationKeys);
// get a long list of existing keys from en.js
var enJson = JSON.parse(fs.readFileSync(path.join(cwd, 'assets', 'i18n', 'en.json')).toString());
function flattenAllKeys(obj, parentKey) {
var result = [];
for (var k in obj) {
if (typeof obj[k] == 'object' && obj[k] !== null) {
var newParent = typeof parentKey !== 'undefined' ? (parentKey + '.' + k) : k;
result = result.concat(flattenAllKeys(obj[k], newParent));
} else if (typeof obj[k] == 'string'
&& parentKey.indexOf('TEMPDEMOPAGES') === -1) {
result[result.length] = parentKey + '.' + k;
}
}
return result;
}
var allENKeys = flattenAllKeys(enJson);
// find unused keys
function getUnused() {
allENKeys.forEach(k => {
var parentKey = k.split('.');
parentKey.pop();
parentKey = parentKey.join('.');
if (typeof translationKeys[parentKey] === 'undefined' ||
translationKeys[parentKey].indexOf(k) === -1) {
console.log('Unused key: ' + k);
}
});
}
function getMissing() {
for (var k in translationKeys) {
if (typeof translationKeys[k] !== 'undefined') {
translationKeys[k].forEach(i => {
if (allENKeys.indexOf(i) === -1) {
console.log('Missing key: ' + i);
}
});
}
}
}
getMissing();
getUnused();
// find strings of text in html files
var htmlWordRegex = (/>[^><]*?(\b[^><]*\b)+[^><]*?</ig)
var attrWordRegex = (/(placeholder|title|alt)\s*=\s*["]([^"]*)["]/ig);
var needTranslations = files.map((f, i) => {
var html = fs.readFileSync(path.join(cwd, f)).toString();
let r;
var needTranslations = [];
while ((r = htmlWordRegex.exec(html)) !== null) {
if (r[1].trim() !== '' && r[1].match(/\|\s*translate/ig) === null) {
needTranslations[needTranslations.length] = r[1].trim();
}
}
while ((r = attrWordRegex.exec(html)) !== null) {
if (r[2].match(/\|\s*translate/ig) === null) {
needTranslations[needTranslations.length] = r[2].trim();
}
}
return {file: f, texts: needTranslations};
}).filter(t => t.texts.length > 0);
needTranslations.forEach(t => {
console.log('Needs translating: ' + t.texts + ' in ' + t.file);
});
// Import required modules
const fs = require('fs');
const path = require('path');
// Define constants for the current working directory and file extensions
const cwd = '/Users/briancullinan/Documents/portal/src/';
const fileExtension = '.js';
// Define a function to find misplaced keys in objects
function findMisplaced(obj, parentKey) {
/**
* Recursively finds misplaced keys in objects.
*
* @param {object} obj - The object to search in.
* @param {string} parentKey - The parent key to check against.
*/
for (const key in obj) {
if (typeof obj[key] === 'object' && obj[key]!== null) {
const newParent = parentKey? `${parentKey}.${key}` : key;
findMisplaced(obj[key], newParent);
} else if (typeof obj[key] ==='string') {
if (!obj[key].startsWith(parentKey)) {
console.log(`Misplaced key: ${obj[key]} in ${parentKey}`);
}
}
}
}
// Define a function to flatten all keys in objects
function flattenAllKeys(obj, parentKey = '') {
/**
* Recursively flattens all keys in objects.
*
* @param {object} obj - The object to search in.
* @param {string} parentKey - The parent key to append to.
* @returns {string[]} - An array of flattened keys.
*/
const result = [];
for (const key in obj) {
if (typeof obj[key] === 'object' && obj[key]!== null) {
const newParent = parentKey? `${parentKey}.${key}` : key;
result.push(...flattenAllKeys(obj[key], newParent));
} else if (typeof obj[key] ==='string' &&!parentKey.includes('TEMPDEMOPAGES')) {
result.push(`${parentKey}.${key}`);
}
}
return result;
}
// Load the translation keys and English JSON file
const translationKeys = require(path.join(cwd, 'translation.js'));
const enJson = JSON.parse(fs.readFileSync(path.join(cwd, 'assets', 'i18n', 'en.json')).toString());
// Get all English keys
const allENKeys = flattenAllKeys(enJson);
// Find unused keys
function getUnused() {
allENKeys.forEach((key) => {
const parentKey = key.split('.').slice(0, -1).join('.');
if (!translationKeys[parentKey] ||!translationKeys[parentKey].includes(key)) {
console.log(`Unused key: ${key}`);
}
});
}
// Find missing keys
function getMissing() {
for (const key in translationKeys) {
if (translationKeys[key]) {
translationKeys[key].forEach((i) => {
if (allENKeys.indexOf(i) === -1) {
console.log(`Missing key: ${i}`);
}
});
}
}
}
// Find strings of text in HTML files
const htmlWordRegex = />[^><]*?(\b[^><]*\b)+[^><]*?<\/ig;
const attrWordRegex = /(placeholder|title|alt)\s*=\s*["]([^"]*)["]/ig;
const files = ['file1.html', 'file2.html'];
const needTranslations = files.map((f, i) => {
const html = fs.readFileSync(path.join(cwd, f)).toString();
let r;
const needTranslations = [];
while ((r = htmlWordRegex.exec(html))!== null) {
if (r[1].trim()!== '' &&!r[1].match(/\|\s*translate/ig)) {
needTranslations.push(r[1].trim());
}
}
while ((r = attrWordRegex.exec(html))!== null) {
if (!r[2].match(/\|\s*translate/ig)) {
needTranslations.push(r[2].trim());
}
}
return { file: f, texts: needTranslations };
}).filter((t) => t.texts.length > 0);
needTranslations.forEach((t) => {
console.log(`Needs translating: ${t.texts.join(', ')} in ${t.file}`);
});
// Call the functions
findMisplaced(translationKeys);
getMissing();
getUnused();This code snippet analyzes translation keys in a JSON file to identify misplaced, unused, and missing keys.
Here's a breakdown:
1. Setup:
require('path') and require('fs'): Imports modules for working with file paths and file system operations.cwd: Sets the current working directory.findMisplaced(obj, parentKey): A recursive function that traverses an object and identifies keys that don't start with the expected parent key.2. Initial Key Analysis:
translationKeys: Assumed to be a global variable containing the translation keys to be analyzed.
findMisplaced(translationKeys): Calls the function to find misplaced keys in the translationKeys object.
enJson: Reads the contents of en.json (likely a JSON file containing English translations) and parses it into a JavaScript object.
3. Flattening Keys:
flattenAllKeys(obj, parentKey): A recursive function that flattens the key structure of an object into a single array of keys.
allENKeys: Calls flattenAllKeys on the enJson object to get a list of all keys in the English translation file.
4. Key Comparison and Reporting:
getUnused(): Iterates through allENKeys and checks if each key exists in translationKeys. If not, it logs the unused key.
getMissing(): Iterates through translationKeys and checks if each key and its corresponding values exist in allENKeys. If not, it logs the missing key.
In essence, this code helps identify potential issues with translation keys, ensuring consistency and completeness across different language versions.