Files
foundryvtt-wh4-lang-fr-fr/tools/auto-translate-english.js

220 lines
7.4 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 FR_DIR = path.join(__dirname, 'scripts');
const ENGLISH_TEXTS_FILE = path.join(__dirname, 'english-texts-found.json');
const FR_JSON_FILE = path.join(__dirname, 'fr.json');
// Charger les données
const englishTexts = JSON.parse(fs.readFileSync(ENGLISH_TEXTS_FILE, 'utf8'));
const frJson = JSON.parse(fs.readFileSync(FR_JSON_FILE, 'utf8'));
// Créer un mapping de traductions basé sur fr.json et les patterns connus
const translations = {
// Patterns déjà traduits dans le système
"Choose Training": "Choisir un Entraînement",
"Lore (Magic)": "Savoir (Magie)",
"Dark Magic (Necromancy)": "Magie Noire (Nécromancie)",
"Lore (Theology)": "Savoir (Théologie)",
"Lore (Runes)": "Savoir (Runes)",
"Sail (Skycraft)": "Voile (Aéronavale)",
"all forms": "toutes formes",
// Messages utilisateur
"Skipping Tests to apply the tattoos": "Tests ignorés pour appliquer les tatouages",
"Apply Ward of Grimnir effect?": "Appliquer l'effet Rune de Grimnir ?",
"No Lore (Theology) skill found, cannot pass.": "Compétence Savoir (Théologie) introuvable, impossible de continuer.",
"No Lore (Runes) skill found, cannot pass.": "Compétence Savoir (Runes) introuvable, impossible de continuer.",
"One or more Tests to apply the tattoos failed.": "Un ou plusieurs Tests pour appliquer les tatouages ont échoué.",
"Must provide a Master Rune": "Doit fournir une Rune Maîtresse",
"Must provide a Rune (non-Master)": "Doit fournir une Rune (non-Maîtresse)",
// Effets et conditions
"Removed Broken": "Condition Brisé supprimée",
"Added Bleeding": "Saignement ajouté",
"Applied after effects": "Effets secondaires appliqués",
"Does not need to make Peur or Terror tests": "N'a pas besoin de faire de tests de Peur ou de Terreur",
"Automatically passes any": "Réussit automatiquement tout",
// Fragments de code courants (contextuels)
"action-link critical": "action-link critical", // Classe CSS, ne pas traduire
"></i> Critical</a>": "></i> Critique</a>",
// Patterns génériques
"|| this.item.getFlag(": "|| this.item.getFlag(", // Code JS, ne pas traduire
',"info")': ',"info")', // Code JS, ne pas traduire
};
// Fonction pour obtenir la fréquence de chaque texte anglais
function getTextFrequencies() {
const frequencies = new Map();
englishTexts.forEach(({ strings }) => {
strings.forEach(str => {
frequencies.set(str, (frequencies.get(str) || 0) + 1);
});
});
return frequencies;
}
// Fonction pour proposer des traductions automatiques
function proposeTranslations(text) {
// Si déjà dans le mapping, retourner
if (translations[text]) {
return translations[text];
}
// Patterns de remplacement simples basés sur fr.json
let translated = text;
// Mots courants du vocabulaire WFRP
const vocabulary = {
"Choose": "Choisir",
"Select": "Sélectionner",
"Enter": "Entrer",
"Critical": "Critique",
"Wounds": "Blessures",
"Bleeding": "Saignement",
"Broken": "Brisé",
"Removed": "Retiré",
"Added": "Ajouté",
"Applied": "Appliqué",
"Failed": "Échoué",
"Passed": "Réussi",
"Cannot": "Impossible de",
"Must": "Doit",
"Need": "Besoin",
"Automatically": "Automatiquement",
"any": "tout",
"all": "tout",
"Test": "Test",
"Tests": "Tests",
"Skill": "Compétence",
"effect": "effet",
"after effects": "effets secondaires",
};
// Appliquer les remplacements de vocabulaire
for (const [en, fr] of Object.entries(vocabulary)) {
const regex = new RegExp(`\\b${en}\\b`, 'gi');
translated = translated.replace(regex, (match) => {
// Conserver la casse
if (match[0] === match[0].toUpperCase()) {
return fr.charAt(0).toUpperCase() + fr.slice(1);
}
return fr;
});
}
// Si rien n'a changé, ne pas proposer de traduction
if (translated === text) {
return null;
}
return translated;
}
// Analyser et créer le mapping complet
console.log('Création du mapping de traductions...\n');
const frequencies = getTextFrequencies();
const autoTranslations = new Map();
// Trier par fréquence décroissante
const sortedTexts = Array.from(frequencies.entries())
.sort((a, b) => b[1] - a[1]);
let proposedCount = 0;
let existingCount = 0;
sortedTexts.forEach(([text, freq]) => {
if (translations[text]) {
autoTranslations.set(text, translations[text]);
existingCount++;
} else {
const proposed = proposeTranslations(text);
if (proposed && proposed !== text) {
autoTranslations.set(text, proposed);
proposedCount++;
if (proposedCount <= 10) {
console.log(`"${text}" → "${proposed}" (${freq}x)`);
}
}
}
});
console.log(`\n${existingCount} traductions existantes`);
console.log(`${proposedCount} traductions proposées automatiquement`);
console.log(`${sortedTexts.length - existingCount - proposedCount} textes ignorés (code/technique)`);
// Appliquer les traductions
console.log('\n' + '='.repeat(60));
console.log('Application des traductions...\n');
let stats = {
filesProcessed: 0,
filesModified: 0,
replacementsMade: 0,
errors: 0
};
englishTexts.forEach(({ file, strings }) => {
const filePath = path.join(FR_DIR, file);
stats.filesProcessed++;
try {
let content = fs.readFileSync(filePath, 'utf8');
let modified = false;
let replacementsInFile = 0;
strings.forEach(text => {
const translation = autoTranslations.get(text);
if (translation && content.includes(text)) {
// Remplacement simple
const newContent = content.split(text).join(translation);
if (newContent !== content) {
const count = content.split(text).length - 1;
content = newContent;
modified = true;
replacementsInFile += count;
stats.replacementsMade += count;
}
}
});
if (modified) {
fs.writeFileSync(filePath, content, 'utf8');
stats.filesModified++;
console.log(`${file} : ${replacementsInFile} remplacement(s)`);
}
} catch (error) {
stats.errors++;
console.error(`${file} : ${error.message}`);
}
});
console.log('\n' + '='.repeat(60));
console.log('Traduction automatique terminée !');
console.log('='.repeat(60));
console.log(`Fichiers traités : ${stats.filesProcessed}`);
console.log(`Fichiers modifiés : ${stats.filesModified}`);
console.log(`Remplacements effectués : ${stats.replacementsMade}`);
console.log(`Erreurs : ${stats.errors}`);
console.log('='.repeat(60));
// Sauvegarder le mapping de traductions
const mappingFile = path.join(__dirname, 'auto-translations-applied.json');
fs.writeFileSync(
mappingFile,
JSON.stringify(Object.fromEntries(autoTranslations), null, 2),
'utf8'
);
console.log(`\nMapping de traductions sauvegardé dans : auto-translations-applied.json`);