Created
March 12, 2025 10:28
-
-
Save BoDonkey/434cd3ff6915b372879d19e6b0747825 to your computer and use it in GitHub Desktop.
Node.js process for comparing and translating missing key:string values in two JSON files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs'); | |
const path = require('path'); | |
const https = require('https'); | |
/** | |
* Translates text using Google Translate API (free method) | |
* | |
* @param {string} text - Text to translate | |
* @param {string} targetLang - Target language code (e.g., 'es', 'fr', 'de') | |
* @returns {Promise<string>} - Translated text | |
*/ | |
async function translateText(text, targetLang) { | |
return new Promise((resolve, reject) => { | |
// Encode text for URL | |
const encodedText = encodeURIComponent(text); | |
// Google Translate URL | |
const options = { | |
hostname: 'translate.googleapis.com', | |
path: `/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodedText}`, | |
method: 'GET', | |
headers: { | |
'User-Agent': 'Mozilla/5.0' | |
} | |
}; | |
const req = https.request(options, (res) => { | |
let data = ''; | |
res.on('data', (chunk) => { | |
data += chunk; | |
}); | |
res.on('end', () => { | |
try { | |
// Parse response | |
const json = JSON.parse(data); | |
const translatedText = json[0][0][0]; | |
resolve(translatedText); | |
} catch (error) { | |
reject(new Error(`Translation failed: ${error.message}`)); | |
} | |
}); | |
}); | |
req.on('error', (error) => { | |
reject(error); | |
}); | |
req.end(); | |
}); | |
} | |
/** | |
* Gets language code from filename | |
* | |
* @param {string} filename - Filename like 'fr.json', 'pt-BR.json' or 'translations-de.json' | |
* @returns {string} - Language code ('fr', 'pt-BR', 'de', etc.) | |
*/ | |
function getLanguageCodeFromFilename(filename) { | |
// Remove file extension | |
const nameWithoutExt = filename.split('.')[0]; | |
// Extract language code based on common patterns | |
if (filename.startsWith('messages_')) { | |
// For messages_fr.json or messages_pt-BR.json pattern | |
return nameWithoutExt.slice(9); | |
} else if (nameWithoutExt.includes('-') && !['pt-BR', 'zh-CN', 'zh-TW', 'en-US', 'en-GB', 'fr-CA', 'es-MX'].includes(nameWithoutExt)) { | |
// For translations-de.json pattern but not pt-BR.json | |
const parts = nameWithoutExt.split('-'); | |
return parts[parts.length - 1]; | |
} else { | |
// For fr.json or pt-BR.json pattern | |
return nameWithoutExt; | |
} | |
} | |
/** | |
* Synchronizes translation files by finding keys in the English file | |
* that are missing from other language files and adding them. | |
* | |
* @param {string} englishFilePath - Path to the English translation JSON file | |
* @param {string} languagesDir - Directory containing all translation files | |
* @param {boolean} keepEnglishValue - Whether to keep English value as placeholder (true) or use empty string (false) | |
* @param {boolean} autoTranslate - Whether to auto-translate missing strings (true) or not (false) | |
*/ | |
async function synchronizeTranslations(englishFilePath, languagesDir, keepEnglishValue = true, autoTranslate = false) { | |
// Read the English file | |
console.log(`Reading English file: ${englishFilePath}`); | |
const englishContent = fs.readFileSync(englishFilePath, 'utf8'); | |
const englishData = JSON.parse(englishContent); | |
// Get all translation files | |
const files = fs.readdirSync(languagesDir) | |
.filter(file => file.endsWith('.json') && !file.endsWith('en.json')); | |
console.log(`Found ${files.length} non-English translation files`); | |
// Process each non-English file | |
for (const file of files) { | |
const filePath = path.join(languagesDir, file); | |
console.log(`Processing: ${file}`); | |
// Read the language file | |
const langContent = fs.readFileSync(filePath, 'utf8'); | |
const langData = JSON.parse(langContent); | |
// Find missing keys | |
const missingKeys = []; | |
let addedCount = 0; | |
// Get language code from filename | |
const langCode = getLanguageCodeFromFilename(file); | |
console.log(`Language code detected: ${langCode}`); | |
for (const key in englishData) { | |
if (!langData.hasOwnProperty(key)) { | |
missingKeys.push(key); | |
if (autoTranslate) { | |
try { | |
// Translate the English value | |
const englishValue = englishData[key]; | |
console.log(`Translating: "${englishValue}" to ${langCode}`); | |
// Add delay to avoid rate limiting | |
await new Promise(resolve => setTimeout(resolve, 1000)); | |
const translatedValue = await translateText(englishValue, langCode); | |
console.log(`Translation result: "${translatedValue}"`); | |
langData[key] = translatedValue; | |
} catch (error) { | |
console.error(`Translation error for key "${key}": ${error.message}`); | |
// Fallback to English or empty string | |
langData[key] = keepEnglishValue ? englishData[key] : ''; | |
} | |
} else { | |
// Add the missing key with either English value or empty string | |
langData[key] = keepEnglishValue ? englishData[key] : ''; | |
} | |
addedCount++; | |
} | |
} | |
if (missingKeys.length > 0) { | |
console.log(`Found ${missingKeys.length} missing keys in ${file}:`); | |
missingKeys.forEach(key => { | |
console.log(` - ${key}: ${englishData[key]}`); | |
}); | |
// Write the updated language file | |
fs.writeFileSync(filePath, JSON.stringify(langData, null, 2), 'utf8'); | |
console.log(`Updated ${file} with ${addedCount} new translations`); | |
} else { | |
console.log(`No missing keys found in ${file}`); | |
} | |
} | |
console.log('Translation synchronization complete!'); | |
} | |
// Example usage | |
// synchronizeTranslations('./locales/en.json', './locales', true, true); | |
// For command line usage | |
if (require.main === module) { | |
const args = process.argv.slice(2); | |
if (args.length < 2) { | |
console.log('Usage: node sync-translations.js <english-file-path> <languages-directory> [keep-english-value(true/false)] [auto-translate(true/false)]'); | |
process.exit(1); | |
} | |
const englishFilePath = args[0]; | |
const languagesDir = args[1]; | |
const keepEnglishValue = args.length > 2 ? args[2].toLowerCase() === 'true' : true; | |
const autoTranslate = args.length > 3 ? args[3].toLowerCase() === 'true' : false; | |
synchronizeTranslations(englishFilePath, languagesDir, keepEnglishValue, autoTranslate) | |
.catch(error => { | |
console.error('Error:', error.message); | |
process.exit(1); | |
}); | |
} | |
module.exports = { synchronizeTranslations }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment