feat: auto generate translations (#288)
* add cloud translation api package * feat: Script that auto generates translations for other languages * add comments * fix: broken translation * improve translation * update locck file * clean up * all round trasnlations check * checkpoint * finish up * move to dev dependencies * fix vercel build error --------- Co-authored-by: Rotimi Best <best@classroomio.com>
This commit is contained in:
co-authored by
Rotimi Best
parent
72f83a4365
commit
648e15d88d
@@ -0,0 +1,141 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
|
||||
const axios = require('axios');
|
||||
const dotenv = require('dotenv');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const keys = require('all-object-keys');
|
||||
const jessy = require('jessy');
|
||||
const set = require('lodash/set');
|
||||
|
||||
const englishTranslations = require('../src/lib/utils/translations/en.json');
|
||||
|
||||
// Load env variables
|
||||
dotenv.config();
|
||||
|
||||
const SCRIPT_WAIT_TIME = 2000;
|
||||
|
||||
// Define file paths for each language
|
||||
const languageFiles = {
|
||||
hi: path.resolve(__dirname, '../src/lib/utils/translations/hi.json'),
|
||||
fr: path.resolve(__dirname, '../src/lib/utils/translations/fr.json'),
|
||||
pt: path.resolve(__dirname, '../src/lib/utils/translations/pt.json'),
|
||||
de: path.resolve(__dirname, '../src/lib/utils/translations/de.json'),
|
||||
vi: path.resolve(__dirname, '../src/lib/utils/translations/vi.json'),
|
||||
ru: path.resolve(__dirname, '../src/lib/utils/translations/re.json'),
|
||||
es: path.resolve(__dirname, '../src/lib/utils/translations/es.json')
|
||||
};
|
||||
|
||||
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
const getMissingTranslations = async (toLanguage, outputFilePath) => {
|
||||
const englishKeys = keys(englishTranslations);
|
||||
const targetTranslations = require(outputFilePath); // Load target language translations
|
||||
const targetKeys = keys(targetTranslations);
|
||||
|
||||
const missingKeys = englishKeys.filter((key) => !targetKeys.includes(key));
|
||||
|
||||
const missingKeysWithDetails = {};
|
||||
missingKeys.forEach((key) => {
|
||||
const value = jessy(key, englishTranslations);
|
||||
if (value !== undefined) {
|
||||
missingKeysWithDetails[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Details of missing keys in ${toLanguage.toUpperCase()}:`);
|
||||
console.log(missingKeysWithDetails, 'missingKeysWithDetails');
|
||||
|
||||
// Check if there are missing translations, if not, stop the process
|
||||
if (!missingKeysWithDetails) {
|
||||
console.log(
|
||||
`No missing translations for ${toLanguage.toUpperCase()}. Stopping translation process.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
return missingKeysWithDetails;
|
||||
};
|
||||
|
||||
const translateLanguage = async (fromLanguage, toLanguage, outputFilePath) => {
|
||||
// Check if there are missing translations
|
||||
const missingTranslations = await getMissingTranslations(toLanguage, outputFilePath);
|
||||
console.log('Missing translations', missingTranslations);
|
||||
|
||||
if (!Object.keys(missingTranslations).length) {
|
||||
console.log('No missing translation skip');
|
||||
return;
|
||||
}
|
||||
|
||||
const targetTranslations = require(outputFilePath); // Load target language translations
|
||||
|
||||
const flattenedEnglish = flattenJSON(missingTranslations);
|
||||
const jsonString = JSON.stringify(flattenedEnglish);
|
||||
|
||||
const encodedParams = new URLSearchParams();
|
||||
encodedParams.set('from', fromLanguage);
|
||||
encodedParams.set('to', toLanguage);
|
||||
encodedParams.set('json', jsonString);
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: 'https://google-translate113.p.rapidapi.com/api/v1/translator/json',
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'X-RapidAPI-Key': process.env.RAPID_API_KEY,
|
||||
'X-RapidAPI-Host': 'google-translate113.p.rapidapi.com'
|
||||
},
|
||||
data: encodedParams
|
||||
};
|
||||
|
||||
try {
|
||||
console.log('Calling API');
|
||||
const response = await axios.request(options);
|
||||
const { trans } = response.data;
|
||||
|
||||
console.log('Translation gotten', trans);
|
||||
|
||||
if (trans) {
|
||||
for (const key in trans) {
|
||||
const value = trans[key];
|
||||
set(targetTranslations, key, value);
|
||||
}
|
||||
|
||||
fs.writeFileSync(outputFilePath, JSON.stringify(targetTranslations, null, 2));
|
||||
console.log(`${toLanguage.toUpperCase()} translations updated successfully.`);
|
||||
} else {
|
||||
console.log(`Failed to update ${toLanguage.toUpperCase()} translations.`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error translating to ${toLanguage.toUpperCase()}:`, error);
|
||||
}
|
||||
};
|
||||
|
||||
// Function to flatten the JSON object
|
||||
const flattenJSON = (obj, prefix = '') => {
|
||||
let flattened = {};
|
||||
for (const key in obj) {
|
||||
if (typeof obj[key] === 'object') {
|
||||
const nested = flattenJSON(obj[key], `${prefix}${key}.`);
|
||||
flattened = { ...flattened, ...nested };
|
||||
} else {
|
||||
flattened[`${prefix}${key}`] = obj[key];
|
||||
}
|
||||
}
|
||||
return flattened;
|
||||
};
|
||||
|
||||
// Loop through each language and translate the English text with a delay
|
||||
const translate = async () => {
|
||||
for (const [language, filePath] of Object.entries(languageFiles)) {
|
||||
console.log(`============FROM: EN================`);
|
||||
console.log(`============TO: ${language.toUpperCase()}================`);
|
||||
|
||||
await wait(SCRIPT_WAIT_TIME);
|
||||
await translateLanguage('en', language, filePath);
|
||||
|
||||
console.log(`============COMPLETE================\n\n`);
|
||||
}
|
||||
};
|
||||
|
||||
translate();
|
||||
Reference in New Issue
Block a user