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.