forked from public/foundryvtt-wh4-lang-fr-fr
Ajout de la commande /voyage et grosse MAJK de la commande /auberge
This commit is contained in:
216
tools/extract-translations.js
Normal file
216
tools/extract-translations.js
Normal file
@@ -0,0 +1,216 @@
|
||||
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));
|
||||
Reference in New Issue
Block a user