import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const WFRP4E_DIR = '/home/morr/work/foundryvtt/WFRP4e-FoundryVTT/scripts'; const FR_DIR = path.join(__dirname, 'scripts'); // Fonction pour extraire les chaînes de caractères d'un fichier function extractStrings(content) { const strings = new Set(); // Patterns pour capturer les chaînes entre guillemets const patterns = [ /"([^"]{3,})"/g, // Double quotes /'([^']{3,})'/g, // Single quotes /`([^`]{3,})`/g // Backticks (sans interpolation) ]; patterns.forEach(pattern => { const matches = content.matchAll(pattern); for (const match of matches) { const str = match[1].trim(); // Ignorer les chaînes trop courtes, les nombres, les IDs techniques if (str.length > 2 && !str.match(/^[0-9a-f]{16}$/i) && !str.match(/^[0-9]+$/) && !str.match(/^[a-z]+$/)) { strings.add(str); } } }); return Array.from(strings); } // Comparer deux fichiers et extraire les traductions function compareFiles(enFile, frFile) { const enContent = fs.readFileSync(enFile, 'utf8'); const frContent = fs.readFileSync(frFile, 'utf8'); const enStrings = extractStrings(enContent); const frStrings = extractStrings(frContent); const translations = []; // Chercher les différences qui pourraient être des traductions enStrings.forEach(enStr => { // Si la chaîne anglaise n'est pas dans le fichier FR if (!frStrings.includes(enStr)) { // Chercher une chaîne FR qui pourrait correspondre // (même longueur approximative, même structure) const possibleTranslations = frStrings.filter(frStr => { // Vérifier si la structure est similaire const lengthRatio = frStr.length / enStr.length; return lengthRatio > 0.5 && lengthRatio < 2.0; }); if (possibleTranslations.length > 0) { // Pour l'instant, on stocke toutes les possibilités possibleTranslations.forEach(frStr => { translations.push({ en: enStr, fr: frStr }); }); } } }); return translations; } // Analyser tous les fichiers function analyzeAllScripts() { const enFiles = fs.readdirSync(WFRP4E_DIR).filter(f => f.endsWith('.js')); const frFiles = fs.readdirSync(FR_DIR).filter(f => f.endsWith('.js')); const allTranslations = new Map(); // en -> Set(fr) let filesCompared = 0; let filesWithTranslations = 0; console.log('Analyse des scripts...\n'); enFiles.forEach(file => { if (frFiles.includes(file)) { filesCompared++; const enPath = path.join(WFRP4E_DIR, file); const frPath = path.join(FR_DIR, file); const translations = compareFiles(enPath, frPath); if (translations.length > 0) { filesWithTranslations++; translations.forEach(({ en, fr }) => { if (!allTranslations.has(en)) { allTranslations.set(en, new Set()); } allTranslations.get(en).add(fr); }); } } }); console.log(`Fichiers comparés : ${filesCompared}`); console.log(`Fichiers avec traductions détectées : ${filesWithTranslations}`); console.log(`Paires de traduction uniques : ${allTranslations.size}\n`); return allTranslations; } // Filtrer les traductions cohérentes function filterReliableTranslations(translationsMap) { const reliable = new Map(); for (const [en, frSet] of translationsMap.entries()) { // Si une chaîne anglaise a toujours la même traduction, c'est fiable if (frSet.size === 1) { reliable.set(en, Array.from(frSet)[0]); } // Si elle a plusieurs traductions mais une dominante (>50%), on la garde else if (frSet.size <= 3) { const frArray = Array.from(frSet); reliable.set(en, frArray[0]); // Pour l'instant, on prend la première } } return reliable; } // Chercher les occurrences dans les scripts FR function findUntranslatedInFR(translations) { const frFiles = fs.readdirSync(FR_DIR).filter(f => f.endsWith('.js')); const opportunities = []; console.log('Recherche des chaînes non traduites dans les scripts FR...\n'); frFiles.forEach(file => { const filePath = path.join(FR_DIR, file); const content = fs.readFileSync(filePath, 'utf8'); for (const [en, fr] of translations.entries()) { if (content.includes(en)) { opportunities.push({ file, en, fr }); } } }); return opportunities; } // Main console.log('Extraction des traductions déjà effectuées...\n'); console.log('='.repeat(60) + '\n'); const allTranslations = analyzeAllScripts(); const reliableTranslations = filterReliableTranslations(allTranslations); console.log('='.repeat(60)); console.log(`Traductions fiables identifiées : ${reliableTranslations.size}\n`); // Afficher un échantillon let count = 0; console.log('Échantillon des traductions détectées :'); console.log('-'.repeat(60)); for (const [en, fr] of reliableTranslations.entries()) { if (count++ < 20) { console.log(`"${en}" → "${fr}"`); } } if (reliableTranslations.size > 20) { console.log(`... et ${reliableTranslations.size - 20} autres\n`); } // Chercher les opportunités d'application console.log('\n' + '='.repeat(60)); const opportunities = findUntranslatedInFR(reliableTranslations); console.log(`Opportunités de traduction trouvées : ${opportunities.length}\n`); // Regrouper par fichier const byFile = new Map(); opportunities.forEach(({ file, en, fr }) => { if (!byFile.has(file)) { byFile.set(file, []); } byFile.get(file).push({ en, fr }); }); console.log('Fichiers avec opportunités de traduction :'); console.log('-'.repeat(60)); for (const [file, trans] of byFile.entries()) { console.log(`${file} : ${trans.length} traduction(s) à appliquer`); } // Sauvegarder les résultats const results = { reliableTranslations: Object.fromEntries(reliableTranslations), opportunities: Array.from(byFile.entries()).map(([file, trans]) => ({ file, translations: trans })) }; fs.writeFileSync( path.join(__dirname, 'translation-opportunities.json'), JSON.stringify(results, null, 2), 'utf8' ); console.log('\n' + '='.repeat(60)); console.log('Résultats sauvegardés dans translation-opportunities.json'); console.log('='.repeat(60));