Files
foundryvtt-wh4-lang-fr-fr/tools/extract-translations.js

217 lines
6.8 KiB
JavaScript

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));