138 lines
5.0 KiB
JavaScript
138 lines
5.0 KiB
JavaScript
import fs from 'fs';
|
|
import axios from 'axios';
|
|
import path from 'path';
|
|
|
|
// Function to load a language file dynamically
|
|
const loadLanguageFile = async (langFile) => {
|
|
const filePath = path.resolve(langFile);
|
|
const langModule = await import(filePath);
|
|
return langModule;
|
|
};
|
|
|
|
// Helper function to recursively generate the object structure
|
|
const generateObjectString = (source, target, parentPath = 'en') => {
|
|
let result = '{\n';
|
|
for (const key in source) {
|
|
if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
result += ` "${key}": {\n ...${parentPath}.${key},\n`;
|
|
result += generateObjectString(source[key], target[key] || {}, `${parentPath}.${key}`).slice(1, -1);
|
|
result += ' },\n';
|
|
} else {
|
|
const translatedValue = target[key] ? JSON.stringify(target[key]) : JSON.stringify(source[key]);
|
|
result += ` "${key}": ${translatedValue},\n`;
|
|
}
|
|
}
|
|
result += '}';
|
|
return result;
|
|
};
|
|
|
|
// Function to save the updated language file
|
|
const saveUpdatedFile = (lang, updatedContent, originalContent) => {
|
|
const filePath = path.join(process.cwd(), `${lang}-updated.js`);
|
|
const fileContent = `
|
|
import { en } from "./en.js";
|
|
export const ${lang} = {
|
|
...en,
|
|
${generateObjectString(originalContent, updatedContent).slice(1, -1)}
|
|
};
|
|
`;
|
|
fs.writeFileSync(filePath, fileContent);
|
|
console.log(`Updated file saved as ${lang}-updated.js`);
|
|
};
|
|
|
|
// Function to replace placeholders with markers before translation
|
|
const protectPlaceholders = (text) => {
|
|
const placeholders = [];
|
|
const protectedText = text.replace(/{[^}]+}/g, (match) => {
|
|
placeholders.push(match);
|
|
return `___PLACEHOLDER${placeholders.length - 1}___`;
|
|
});
|
|
return { protectedText, placeholders };
|
|
};
|
|
|
|
// Function to restore placeholders after translation
|
|
const restorePlaceholders = (translatedText, placeholders) => {
|
|
return translatedText.replace(/___PLACEHOLDER(\d+)___/g, (_, index) => placeholders[index]);
|
|
};
|
|
|
|
// DeepL Translation function with retry logic and placeholder protection
|
|
const translateText = async (text, targetLang, deeplApiKey, retryCount = 3) => {
|
|
const { protectedText, placeholders } = protectPlaceholders(text);
|
|
|
|
try {
|
|
const response = await axios({
|
|
method: 'post',
|
|
url: 'https://api.deepl.com/v2/translate',
|
|
params: {
|
|
auth_key: deeplApiKey,
|
|
text: protectedText,
|
|
target_lang: targetLang.toUpperCase(),
|
|
},
|
|
timeout: 10000 // Timeout of 10 seconds
|
|
});
|
|
|
|
const translatedText = response.data.translations[0].text;
|
|
return restorePlaceholders(translatedText, placeholders);
|
|
} catch (error) {
|
|
if (retryCount > 0) {
|
|
console.log(`Retrying translation for text: "${text}". Retries left: ${retryCount}`);
|
|
return translateText(text, targetLang, deeplApiKey, retryCount - 1);
|
|
} else {
|
|
console.error(`Failed to translate text: "${text}" after multiple attempts`);
|
|
throw error;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Recursive function to find and translate missing keys
|
|
const findMissingTranslations = async (enData, targetData, targetLang, deeplApiKey) => {
|
|
const updatedData = { ...targetData };
|
|
|
|
const recursiveCheck = async (source, target) => {
|
|
for (const key in source) {
|
|
if (typeof source[key] === 'object') {
|
|
if (!target[key]) target[key] = {};
|
|
await recursiveCheck(source[key], target[key]);
|
|
} else {
|
|
if (!target[key] || target[key] === source[key]) {
|
|
console.log(`Translating key: ${key}`);
|
|
const translatedText = await translateText(source[key], targetLang, deeplApiKey);
|
|
target[key] = translatedText;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
await recursiveCheck(enData, updatedData);
|
|
return updatedData;
|
|
};
|
|
|
|
// Main function to run the translation process
|
|
const translateMissingKeys = async (targetLang, langFile, deeplApiKey) => {
|
|
|
|
// Load the English and target language files
|
|
const enModule = await loadLanguageFile('./en.js');
|
|
const targetModule = await loadLanguageFile(langFile);
|
|
|
|
const enData = enModule.en;
|
|
const targetData = targetModule[targetLang];
|
|
|
|
// Find missing translations and translate them
|
|
const updatedData = await findMissingTranslations(enData, targetData, targetLang, deeplApiKey);
|
|
|
|
// Save the updated target language file with proper structure
|
|
saveUpdatedFile(targetLang, updatedData, enData);
|
|
};
|
|
|
|
// Run the script with a given language argument and filename argument
|
|
const args = process.argv.slice(2);
|
|
if (args.length < 2) {
|
|
console.error('Please provide a target language code and a file name, e.g., node translate.js de ./de.js');
|
|
process.exit(1);
|
|
}
|
|
|
|
const targetLang = args[0];
|
|
const langFile = args[1];
|
|
const deeplApiKey = args[2];
|
|
translateMissingKeys(targetLang, langFile, deeplApiKey);
|