forked from public/foundryvtt-wh4-lang-fr-fr
220 lines
7.4 KiB
JavaScript
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`);
|