Ajout de la commande /voyage et grosse MAJK de la commande /auberge

This commit is contained in:
2026-01-07 15:04:49 +01:00
parent c8119601d8
commit f525b6c07a
1329 changed files with 24138 additions and 4397 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,100 @@
import fs from 'fs';
// Lire le fichier JSON
const jsonData = JSON.parse(fs.readFileSync('./compendium/wfrp4e-core.items.json', 'utf8'));
const frJson = JSON.parse(fs.readFileSync('./fr.json', 'utf8'));
// Chercher les termes en anglais (labels et titres courts)
const englishTerms = new Set();
const termOccurrences = {};
// Parcourir les entrées
jsonData.entries.forEach(entry => {
// Vérifier le champ 'name' pour des patterns entre parenthèses
if (entry.name && entry.name.includes('(') && entry.name.includes(')')) {
const matches = entry.name.matchAll(/\(([^)]+)\)/g);
for (const match of matches) {
const parenthesisContent = match[1];
// Liste de termes français connus à exclure
const frenchTerms = [
'Mineure', 'Majeure', 'Accessible', 'Complexe', 'Difficile',
'Facile', 'Intermédiaire', 'Type', 'Domaine', 'Arme de jet',
'Arme de mêlée', 'Compétence avancée', 'Compétence de base'
];
// Détecter si c'est potentiellement de l'anglais
// - Contient des mots anglais courants (et, the, or, of)
// - Ne contient pas de caractères français (é, è, à, ô, etc.)
const hasEnglishWords = /\b(and|the|or|of|to|in|a|is)\b/i.test(parenthesisContent);
const hasNoFrenchChars = !/[àâäéèêëïîôùûüÿæœç]/i.test(parenthesisContent);
const notInFrenchList = !frenchTerms.some(term =>
parenthesisContent.toLowerCase().includes(term.toLowerCase())
);
if ((hasEnglishWords || (hasNoFrenchChars && notInFrenchList)) &&
parenthesisContent.length > 2) {
englishTerms.add(parenthesisContent);
if (!termOccurrences[parenthesisContent]) {
termOccurrences[parenthesisContent] = [];
}
termOccurrences[parenthesisContent].push({
id: entry.id,
name: entry.name
});
}
}
}
});
// Afficher les résultats
console.log('Termes anglais détectés dans les noms (entre parenthèses):');
console.log('==========================================================\n');
const sortedTerms = Array.from(englishTerms).sort();
sortedTerms.forEach(term => {
console.log(`${term} (${termOccurrences[term].length} occurrence(s)):`);
termOccurrences[term].slice(0, 3).forEach(occ => {
console.log(` - ${occ.id}: ${occ.name}`);
});
if (termOccurrences[term].length > 3) {
console.log(` ... et ${termOccurrences[term].length - 3} autres`);
}
console.log('');
});
console.log(`\nTotal: ${englishTerms.size} termes anglais différents détectés`);
// Chercher aussi dans d'autres champs
console.log('\n\nAnalyse des autres champs potentiels...');
console.log('========================================\n');
// Chercher des labels courts en anglais dans tous les champs
const fieldsToCheck = ['type', 'special', 'description'];
const englishInFields = {};
jsonData.entries.slice(0, 20).forEach(entry => {
Object.keys(entry).forEach(key => {
if (typeof entry[key] === 'string' && entry[key].length < 50) {
const value = entry[key];
// Détecter des mots anglais courants
if (/\b(Rating|Target|Group|Feature|Type|Level|Rank)\b/.test(value)) {
if (!englishInFields[key]) {
englishInFields[key] = new Set();
}
englishInFields[key].add(value);
}
}
});
});
Object.keys(englishInFields).forEach(field => {
console.log(`Champ '${field}':`);
englishInFields[field].forEach(value => {
console.log(` - "${value}"`);
});
console.log('');
});

157
tools/apply-sync.js Normal file
View File

@@ -0,0 +1,157 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const WFRP4E_SCRIPTS = '/home/morr/work/foundryvtt/WFRP4e-FoundryVTT/scripts';
const FR_SCRIPTS = '/home/morr/work/foundryvtt/foundryvtt-wh4-lang-fr-fr/scripts';
const DATA_FILE = path.join(__dirname, 'script-comparison-data.json');
const LOG_FILE = path.join(__dirname, 'sync-scripts-log.txt');
// Charger les résultats de l'analyse
const data = JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8'));
const log = [];
function writeLog(message) {
console.log(message);
log.push(message);
}
function deleteObsoleteScripts() {
writeLog('\n=== SUPPRESSION DES SCRIPTS OBSOLÈTES ===\n');
let deleted = 0;
data.usesLocalize.forEach(({file}) => {
const filePath = path.join(FR_SCRIPTS, file);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
deleted++;
writeLog(`✓ Supprimé: ${file}`);
}
});
writeLog(`\nTotal supprimés: ${deleted}/${data.usesLocalize.length}`);
return deleted;
}
function copyNewScripts() {
writeLog('\n=== COPIE DES NOUVEAUX SCRIPTS ===\n');
let copied = 0;
data.onlyInWFRP4E.forEach(file => {
const sourcePath = path.join(WFRP4E_SCRIPTS, file);
const destPath = path.join(FR_SCRIPTS, file);
if (fs.existsSync(sourcePath)) {
fs.copyFileSync(sourcePath, destPath);
copied++;
writeLog(`✓ Copié: ${file}`);
} else {
writeLog(`✗ Source non trouvée: ${file}`);
}
});
writeLog(`\nTotal copiés: ${copied}/${data.onlyInWFRP4E.length}`);
return copied;
}
function deleteRemovedScripts() {
writeLog('\n=== SUPPRESSION DES SCRIPTS RETIRÉS ===\n');
let deleted = 0;
data.onlyInFR.forEach(file => {
const filePath = path.join(FR_SCRIPTS, file);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
deleted++;
writeLog(`✓ Supprimé (n'existe plus dans WFRP4E): ${file}`);
}
});
writeLog(`\nTotal supprimés: ${deleted}/${data.onlyInFR.length}`);
return deleted;
}
function updateScripts() {
writeLog('\n=== MISE À JOUR DES SCRIPTS ===\n');
writeLog('Ces scripts ont été modifiés dans WFRP4E mais contiennent peut-être des traductions FR.');
writeLog('Ils nécessitent une révision manuelle.\n');
let updated = 0;
let needsReview = [];
data.needsUpdate.forEach(item => {
const wfrp4ePath = path.join(WFRP4E_SCRIPTS, item.file);
const frPath = path.join(FR_SCRIPTS, item.file);
const wfrp4eContent = fs.readFileSync(wfrp4ePath, 'utf-8');
const frContent = fs.readFileSync(frPath, 'utf-8');
// Vérifier si le script FR contient des textes en français
const hasFrenchText = /["'`](Chargement|Impossible|Voulez-vous|Êtes-vous|créé|modifié|supprimé)/i.test(frContent);
if (hasFrenchText) {
// Script avec traductions, nécessite révision manuelle
needsReview.push({
file: item.file,
reason: 'Contient des textes traduits en français'
});
writeLog(`⚠ RÉVISION NÉCESSAIRE: ${item.file} (contient des traductions)`);
} else {
// Pas de traductions détectées, on peut mettre à jour automatiquement
fs.copyFileSync(wfrp4ePath, frPath);
updated++;
writeLog(`✓ Mis à jour: ${item.file}`);
}
});
writeLog(`\nTotal mis à jour automatiquement: ${updated}`);
writeLog(`Total nécessitant révision: ${needsReview.length}`);
// Créer un fichier avec la liste des scripts à réviser
if (needsReview.length > 0) {
const reviewFile = path.join(__dirname, 'scripts-to-review.json');
fs.writeFileSync(reviewFile, JSON.stringify(needsReview, null, 2), 'utf-8');
writeLog(`\nListe des scripts à réviser sauvegardée dans: ${reviewFile}`);
}
return {updated, needsReview: needsReview.length};
}
// Programme principal
writeLog('='.repeat(60));
writeLog('SYNCHRONISATION DES SCRIPTS WFRP4E');
writeLog('='.repeat(60));
writeLog(`Date: ${new Date().toISOString()}\n`);
writeLog('Statistiques:');
writeLog(` - Scripts identiques: ${data.identical.length}`);
writeLog(` - Scripts obsolètes (localize): ${data.usesLocalize.length}`);
writeLog(` - Scripts à mettre à jour: ${data.needsUpdate.length}`);
writeLog(` - Nouveaux scripts (WFRP4E): ${data.onlyInWFRP4E.length}`);
writeLog(` - Scripts à supprimer (FR): ${data.onlyInFR.length}`);
// Demander confirmation
console.log('\nVoulez-vous continuer ? (appuyez sur Entrée pour continuer, Ctrl+C pour annuler)');
// Pour l'automatisation, on continue directement
// En production, on utiliserait readline pour une confirmation interactive
const obsoleteDeleted = deleteObsoleteScripts();
const removedDeleted = deleteRemovedScripts();
const newCopied = copyNewScripts();
const {updated, needsReview} = updateScripts();
writeLog('\n' + '='.repeat(60));
writeLog('RÉSUMÉ DES OPÉRATIONS');
writeLog('='.repeat(60));
writeLog(`Scripts obsolètes supprimés: ${obsoleteDeleted}`);
writeLog(`Scripts retirés supprimés: ${removedDeleted}`);
writeLog(`Nouveaux scripts copiés: ${newCopied}`);
writeLog(`Scripts mis à jour automatiquement: ${updated}`);
writeLog(`Scripts nécessitant révision manuelle: ${needsReview}`);
writeLog('='.repeat(60));
// Sauvegarder le log
fs.writeFileSync(LOG_FILE, log.join('\n'), 'utf-8');
writeLog(`\nLog sauvegardé dans: ${LOG_FILE}`);

View File

@@ -0,0 +1,78 @@
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 OPPORTUNITIES_FILE = path.join(__dirname, 'translation-opportunities.json');
// Charger les opportunités de traduction
const data = JSON.parse(fs.readFileSync(OPPORTUNITIES_FILE, 'utf8'));
const { reliableTranslations, opportunities } = data;
// Fonction pour échapper les caractères spéciaux pour regex
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// Statistiques
let stats = {
filesProcessed: 0,
filesModified: 0,
replacementsMade: 0,
errors: 0
};
console.log('Application automatique des traductions...\n');
console.log('='.repeat(60));
// Traiter chaque fichier avec des opportunités
opportunities.forEach(({ file, translations }) => {
const filePath = path.join(FR_DIR, file);
stats.filesProcessed++;
try {
let content = fs.readFileSync(filePath, 'utf8');
let modified = false;
let replacementsInFile = 0;
// Appliquer chaque traduction
translations.forEach(({ en, fr }) => {
// Vérifier que la chaîne anglaise existe toujours dans le fichier
if (content.includes(en)) {
// Remplacement simple (pas de regex pour éviter les problèmes avec les caractères spéciaux)
const newContent = content.split(en).join(fr);
if (newContent !== content) {
const count = (content.split(en).length - 1);
content = newContent;
modified = true;
replacementsInFile += count;
stats.replacementsMade += count;
}
}
});
// Sauvegarder si modifié
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('Application 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));

View File

@@ -0,0 +1,116 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const WFRP4E_SCRIPTS = '/home/morr/work/foundryvtt/WFRP4e-FoundryVTT/scripts';
const FR_SCRIPTS = '/home/morr/work/foundryvtt/foundryvtt-wh4-lang-fr-fr/scripts';
const REVIEW_FILE = path.join(__dirname, 'scripts-to-review.json');
const scriptsToReview = JSON.parse(fs.readFileSync(REVIEW_FILE, 'utf-8'));
// Dictionnaire de traductions courantes
const translations = {
'Loading Spells': 'Chargement des sorts',
'Loading': 'Chargement',
'Could not find': 'Impossible de trouver',
'Try Again': 'Essayez à nouveau',
'Error': 'Erreur',
'Success': 'Succès',
'Warning': 'Avertissement',
'Created': 'Créé',
'Modified': 'Modifié',
'Deleted': 'Supprimé',
'Are you sure': 'Êtes-vous sûr',
'Do you want': 'Voulez-vous',
'Cancel': 'Annuler',
'Confirm': 'Confirmer',
'Yes': 'Oui',
'No': 'Non'
};
function translateText(text) {
// Remplacer les traductions connues
let translated = text;
for (const [en, fr] of Object.entries(translations)) {
translated = translated.replace(new RegExp(en, 'g'), fr);
}
return translated;
}
function mergeScript(wfrp4eContent, frContent) {
// Commence avec la version WFRP4E (à jour)
let merged = wfrp4eContent;
// Extraire les textes traduits du script FR
const frTexts = new Map();
const textPattern = /["'`]([^"'`]+)["'`]/g;
let match;
while ((match = textPattern.exec(frContent)) !== null) {
const text = match[1];
// Si le texte contient des mots français caractéristiques
if (/Chargement|Impossible|Êtes-vous|Voulez-vous|créé|modifié|supprimé|Erreur/i.test(text)) {
// Chercher le texte anglais correspondant dans WFRP4E
const enMatch = wfrp4eContent.match(new RegExp(`["'\`]([^"'\`]*${text.split(' ')[0]}[^"'\`]*)["'\`]`));
if (enMatch) {
frTexts.set(enMatch[1], text);
}
}
}
// Appliquer les traductions connues
for (const [en, fr] of frTexts.entries()) {
merged = merged.replace(new RegExp(`["'\`]${en.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}["'\`]`, 'g'), `"${fr}"`);
}
// Appliquer le dictionnaire de traductions
for (const [en, fr] of Object.entries(translations)) {
merged = merged.replace(new RegExp(`["'\`]${en}["'\`]`, 'g'), `"${fr}"`);
}
// Cas spéciaux pour les template literals
merged = merged.replace(/`Could not find \$\{([^}]+)\} spell\. Try Again`/g, '`Impossible de trouver le sort ${$1}. Essayez à nouveau`');
return merged;
}
console.log('Révision semi-automatique des scripts...\n');
let updated = 0;
let skipped = 0;
scriptsToReview.forEach(({file}) => {
const wfrp4ePath = path.join(WFRP4E_SCRIPTS, file);
const frPath = path.join(FR_SCRIPTS, file);
const backupPath = frPath + '.backup';
const wfrp4eContent = fs.readFileSync(wfrp4ePath, 'utf-8');
const frContent = fs.readFileSync(frPath, 'utf-8');
// Sauvegarder l'original
fs.copyFileSync(frPath, backupPath);
// Tenter la fusion
const merged = mergeScript(wfrp4eContent, frContent);
// Vérifier si des traductions ont été préservées
const hasTranslations = /Chargement|Impossible|Êtes-vous|Voulez-vous|créé|modifié|supprimé|Erreur/i.test(merged);
if (hasTranslations) {
fs.writeFileSync(frPath, merged, 'utf-8');
updated++;
console.log(`✓ Mis à jour avec traductions: ${file}`);
} else {
// Restaurer l'original si aucune traduction n'a été préservée
fs.copyFileSync(backupPath, frPath);
skipped++;
console.log(`⚠ Révision manuelle requise: ${file}`);
}
});
console.log(`\n=== Résumé ===`);
console.log(`Scripts mis à jour automatiquement: ${updated}`);
console.log(`Scripts nécessitant révision manuelle: ${skipped}`);
console.log(`\nNOTE: Les fichiers .backup contiennent les versions originales.`);
console.log(`Vérifiez les fichiers mis à jour avant de supprimer les backups.`);

View File

@@ -0,0 +1,219 @@
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`);

View File

@@ -0,0 +1,251 @@
{
"+ this.effect.name\n\nawait this.actor.update(updateObj)\nthis.actor.createEmbeddedDocuments(": "+ this.effet.name\n\nawait this.actor.update(updateObj)\nthis.actor.createEmbeddedDocuments(",
"+ this.effect.name\n\nawait this.actor.update(updateObj)\nconsole.log(": "+ this.effet.name\n\nawait this.actor.update(updateObj)\nconsole.log(",
"Added Bleeding": "Saignement ajouté",
", {skipTargets: true, appendTitle : ` - ${this.effect.name}`, fields : {difficulty :": ", {skipTargets: true, appendTitle : ` - ${this.effet.name}`, fields : {difficulty :",
"Fatigued added to": "Fatigued Ajouté to",
"which cannot be removed until the Malaise symptom is gone.": "which Impossible de be Retiré until the Malaise symptom is gone.",
")}!`}));\n\nthis.effect.updateSource({": ")}!`}));\n\nthis.effet.updateSource({",
"Lore (Magic)": "Savoir (Magie)",
")}) \n}\n\n// Notify of lingering effects when mask is removed\nelse if (!args.equipped)\n{\n await this.item.effects.contents[0].delete();\n await this.item.update({name : this.item.name +=": ")}) \n}\n\n// Notify of lingering effects when mask is Retiré\nelse if (!args.equipped)\n{\n await this.item.effects.contents[0].delete();\n await this.item.update({name : this.item.name +=",
"})\n this.script.message(`<strong>${this.item.name}</strong> on ${this.actor.name} has been taken off and loses its properties. However, the effects last for [[1d10+4]] days, after which they should be manually removed.`, \n {whisper: ChatMessage.getWhisperRecipients(": "})\n this.script.message(`<strong>${this.item.name}</strong> on ${this.actor.name} has been taken off and loses its properties. However, the effects last for [[1d10+4]] days, after which they should be manually Retiré.`, \n {whisper: ChatMessage.getWhisperRecipients(",
",\n {whisper: ChatMessage.getWhisperRecipients(\"GM\")}) \n}\n\n// Notify of lingering effects when mask is removed\nelse if (!args.equipped)\n{\n await this.item.effects.contents[0].delete();\n await this.item.update({name : this.item.name += \" (Used)\"})\n this.script.message(": ",\n {whisper: ChatMessage.getWhisperRecipients(\"GM\")}) \n}\n\n// Notify of lingering effects when mask is Retiré\nelse if (!args.equipped)\n{\n await this.item.effects.contents[0].delete();\n await this.item.update({name : this.item.name += \" (Used)\"})\n this.script.message(",
"+ this.effect.name, context : {failure:": "+ this.effet.name, context : {failure:",
"Choose Characteristic": "Choisir Characteristic",
": this.item.system.tests.value.replace(": ": this.item.system.Tests.value.replace(",
") || this.item.system.tests.value.includes(": ") || this.item.system.Tests.value.includes(",
") || this.item.system.tests.value.toLowerCase().includes(": ") || this.item.system.Tests.value.toLowerCase().includes(",
"))\n{\n let tests = this.item.system.tests.value\n let name = this.item.name\n\n // If name already specifies, make sure tests value reflects that\n if (name.includes(": "))\n{\n let Tests = this.item.system.Tests.value\n let name = this.item.name\n\n // If name already specifies, make sure Tests value reflects that\n if (name.includes(",
",\n skipTargets: true,\n fields: {difficulty: 'easy'},\n characteristic: 'wp',\n});\nawait test.roll();\n\nif (!test.succeeded) {\n test.result.other.push(": ",\n skipTargets: true,\n fields: {difficulty: 'easy'},\n characteristic: 'wp',\n});\nawait Test.roll();\n\nif (!Test.succeeded) {\n Test.result.other.push(",
"Dark Magic (Necromancy)": "Magie Noire (Nécromancie)",
"action-link critical": "action-link critical",
"all forms": "toutes formes",
")})\n}\n\n// Notify of lingering effects when mask is removed\nelse if (!args.equipped)\n{\n await this.item.effects.contents[0].delete();\n await this.item.update({name : this.item.name +=": ")})\n}\n\n// Notify of lingering effects when mask is Retiré\nelse if (!args.equipped)\n{\n await this.item.effects.contents[0].delete();\n await this.item.update({name : this.item.name +=",
";\nconst test = 'Maintenance Crew Test';\nconst difficulty = 'hard';\nconst target = 80;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Maintenance Crew Test';\nconst difficulty = 'hard';\nconst target = 80;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
"}})\nawait test.roll();\nif (test.failed)\n{\n args.actor.addCondition(": "}})\nawait Test.roll();\nif (Test.Échoué)\n{\n args.actor.addCondition(",
"}})\nawait test.roll();\nif (test.failed)\n{\n this.actor.addCondition(": "}})\nawait Test.roll();\nif (Test.Échoué)\n{\n this.actor.addCondition(",
"Choose a Condition": "Choisir a Condition",
"<p>\n Select your choice\n </p>\n <ol>\n <li>Mail</li>\n <li>Mail & Leather</li>\n <li>Plate</li>\n </ol>": "<p>\n Sélectionner your choice\n </p>\n <ol>\n <li>Mail</li>\n <li>Mail & Leather</li>\n <li>Plate</li>\n </ol>",
"})\n await test.roll();\n if (test.failed)\n {\n this.actor.addCondition(": "})\n await Test.roll();\n if (Test.Échoué)\n {\n this.actor.addCondition(",
"critical-roll": "Critique-roll",
").pop())\n\nif (ones > SL)\n args.test.result.other.push(`<span class=": ").pop())\n\nif (ones > SL)\n args.Test.result.other.push(`<span class=",
"Applied after effects": "Effets secondaires appliqués",
"Choose Lore": "Choisir Lore",
")\n }\n else if (this.actor.characteristics.int.bonus + args.test.result.SL < 0)\n args.test.result.other.push(": ")\n }\n else if (this.actor.characteristics.int.bonus + args.Test.result.SL < 0)\n args.Test.result.other.push(",
"Choose Training": "Choisir un Entraînement",
").map(i => {\n return {\n id : i.toLowerCase(),\n name : i\n }\n });\n}\n\nif (choice.length)\n{\n let changes = foundry.utils.deepClone(this.effect.changes);\n\n for(let training of choice)\n {\n switch(training.id)\n {\n case": ").map(i => {\n return {\n id : i.toLowerCase(),\n name : i\n }\n });\n}\n\nif (choice.length)\n{\n let changes = foundry.utils.deepClone(this.effet.changes);\n\n for(let training of choice)\n {\n switch(training.id)\n {\n case",
", [territorial], {fromEffect: this.effect.id})\n foundry.utils.setProperty(args,": ", [territorial], {fromEffect: this.effet.id})\n foundry.utils.setProperty(args,",
"})\n break;\n }\n }\n this.effect.updateSource({name : `${this.effect.name} (${choice.map(i => i.name).join(": "})\n break;\n }\n }\n this.effet.updateSource({name : `${this.effet.name} (${choice.map(i => i.name).join(",
";\n\n let wounds = foundry.utils.duplicate(this.actor.status.wounds);\n let regenRoll = await new Roll(": ";\n\n let Blessures = foundry.utils.duplicate(this.actor.status.Blessures);\n let regenRoll = await new Roll(",
").roll({allowInteractive : false});\n let regen = regenRoll.total;\n\n if (wounds.value >= wounds.max)\n return;\n\n if (wounds.value > 0) {\n wounds.value += Math.floor(regen / 2);\n if (wounds.value > wounds.max) {\n wounds.value = wounds.max;\n }\n message += \\`<b>\\${this.actor.name}</b> regagne \\${regen} Blessures.\\`;\n\n if (regen === 10) {\n message +=": ").roll({allowInteractive : false});\n let regen = regenRoll.total;\n\n if (Blessures.value >= Blessures.max)\n return;\n\n if (Blessures.value > 0) {\n Blessures.value += Math.floor(regen / 2);\n if (Blessures.value > Blessures.max) {\n Blessures.value = Blessures.max;\n }\n message += \\`<b>\\${this.actor.name}</b> regagne \\${regen} Blessures.\\`;\n\n if (regen === 10) {\n message +=",
";\n }\n } else if (regen >= 8) {\n message += \\`<b>\\${this.actor.name}</b> a obtenu un \\${regen} et regagne 1 Blessure.\\`;\n wounds.value += 1;\n if (regen === 10) {\n message +=": ";\n }\n } else if (regen >= 8) {\n message += \\`<b>\\${this.actor.name}</b> a obtenu un \\${regen} et regagne 1 Blessure.\\`;\n Blessures.value += 1;\n if (regen === 10) {\n message +=",
": wounds});\n this.script.message(message, {whisper: ChatMessage.getWhisperRecipients(": ": Blessures});\n this.script.message(message, {whisper: ChatMessage.getWhisperRecipients(",
")});\n`\n\nawait effect.update({\n name,": ")});\n`\n\nawait effet.update({\n name,",
";\n wounds.value += 1;\n if (regen === 10) {\n message += \"<br>De plus, il régénère une Blessure Critique.\";\n }\n } else {\n message += \\": ";\n Blessures.value += 1;\n if (regen === 10) {\n message += \"<br>De plus, il régénère une Blessure Critique.\";\n }\n } else {\n message += \\",
";\n }\n\n await this.actor.update({\"system.status.wounds\": wounds});\n this.script.message(message, {whisper: ChatMessage.getWhisperRecipients(\"GM\")});": ";\n }\n\n await this.actor.update({\"system.status.Blessures\": Blessures});\n this.script.message(message, {whisper: ChatMessage.getWhisperRecipients(\"GM\")});",
")\n}\n\nthis.effect.updateSource({": ")\n}\n\nthis.effet.updateSource({",
")[0].trim()} (${resistance})`})\n this.effect.updateSource({name : this.effect.name + ` (${resistance})`})\n }\n} \nthis.item.updateSource({": ")[0].trim()} (${resistance})`})\n this.effet.updateSource({name : this.effet.name + ` (${resistance})`})\n }\n} \nthis.item.updateSource({",
", resistance)})\n\nif (resistance && !this.effect.name.includes(": ", resistance)})\n\nif (resistance && !this.effet.name.includes(",
"})\n this.effect.updateSource({name : this.effect.name +": "})\n this.effet.updateSource({name : this.effet.name +",
"})\n }\n} \nthis.item.updateSource({\"system.tests.value\" : this.item.system.tests.value.replace(\"the associated Threat\", resistance)})\n\nif (resistance && !this.effect.name.includes(\"(\"))\n{\n this.effect.updateSource({name : this.effect.name +=": "})\n }\n} \nthis.item.updateSource({\"system.Tests.value\" : this.item.system.Tests.value.replace(\"the associated Threat\", resistance)})\n\nif (resistance && !this.effet.name.includes(\"(\"))\n{\n this.effet.updateSource({name : this.effet.name +=",
")\n }\n }\n\n this.effect.updateSource({name})\n this.item.updateSource({name,": ")\n }\n }\n\n this.effet.updateSource({name})\n this.item.updateSource({name,",
"épines\"];\n\nlet updateObj = this.actor.toObject();\n\nfor (let ch in characteristics)\n{\n updateObj.system.characteristics[ch].modifier += characteristics[ch];\n}\n\nfor (let index = 0; index < skills.length; index++)\n{\n let skill = skills[index]\n let skillItem;\n skillItem = updateObj.items.find(i => i.name == skill && i.type == \"skill\")\n if (skillItem)\n skillItem.system.advances.value += skillAdvancements[index]\n else \n {\n skillItem = await game.wfrp4e.utility.findSkill(skill)\n skillItem = skillItem.toObject();\n skillItem.system.advances.value = skillAdvancements[index];\n items.push(skillItem);\n }\n}\n\nfor (let talent of talents)\n{\n let talentItem = await game.wfrp4e.utility.findTalent(talent)\n if (talentItem)\n {\n items.push(talentItem.toObject());\n }\n else \n {\n ui.notifications.warn(`Could not find ${talent}`, {permanent : true})\n }\n}\n\nconst traitRegex = /(?:,?(.+?)(\\+?\\d{1,2}\\+?)?\\s*?(?:\\((.+?)\\)\\s*(\\+?\\d{1,2})?|,|$))/gm\nfor (let trait of traits)\n{\n let traitMatches = trait.matchAll(traitRegex).next().value\n let traitName = traitMatches[1]\n let traitVal = traitMatches[2] || traitMatches[4] // could be match 2 or 4 depending on if there": "épines\"];\n\nlet updateObj = this.actor.toObject();\n\nfor (let ch in characteristics)\n{\n updateObj.system.characteristics[ch].modifier += characteristics[ch];\n}\n\nfor (let index = 0; index < skills.length; index++)\n{\n let Compétence = skills[index]\n let skillItem;\n skillItem = updateObj.items.find(i => i.name == Compétence && i.type == \"Compétence\")\n if (skillItem)\n skillItem.system.advances.value += skillAdvancements[index]\n else \n {\n skillItem = await game.wfrp4e.utility.findSkill(Compétence)\n skillItem = skillItem.toObject();\n skillItem.system.advances.value = skillAdvancements[index];\n items.push(skillItem);\n }\n}\n\nfor (let talent of talents)\n{\n let talentItem = await game.wfrp4e.utility.findTalent(talent)\n if (talentItem)\n {\n items.push(talentItem.toObject());\n }\n else \n {\n ui.notifications.warn(`Could not find ${talent}`, {permanent : true})\n }\n}\n\nconst traitRegex = /(?:,?(.+?)(\\+?\\d{1,2}\\+?)?\\s*?(?:\\((.+?)\\)\\s*(\\+?\\d{1,2})?|,|$))/gm\nfor (let trait of traits)\n{\n let traitMatches = trait.matchAll(traitRegex).next().value\n let traitName = traitMatches[1]\n let traitVal = traitMatches[2] || traitMatches[4] // could be match 2 or 4 depending on if there",
"Skipping Tests to apply the tattoos": "Tests ignorés pour appliquer les tatouages",
"<p>Apply Ward of Grimnir effect?</p>": "<p>Apply Ward of Grimnir effet?</p>",
"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é.",
", fields : {difficulty : \"vhard\"}})\n await test.roll();\n if (test.failed)\n {\n failed = true;\n }\n}\nelse \n{\n this.script.notification(\"No Lore (Theology) skill found, cannot pass.\")\n failed = true;\n}\n\nif (this.actor.itemTags.skill.find(i => i.name == \"Lore (Runes)\"))\n {\n let test = await this.actor.setupSkill(\"Lore (Runes)\", {appendTitle :": ", fields : {difficulty : \"vhard\"}})\n await Test.roll();\n if (Test.Échoué)\n {\n Échoué = true;\n }\n}\nelse \n{\n this.script.notification(\"No Lore (Theology) Compétence found, Impossible de pass.\")\n Échoué = true;\n}\n\nif (this.actor.itemTags.Compétence.find(i => i.name == \"Lore (Runes)\"))\n {\n let Test = await this.actor.setupSkill(\"Lore (Runes)\", {appendTitle :",
", fields : {difficulty : \"hard\"}})\n await test.roll();\n if (test.failed)\n {\n failed = true;\n }\n }\n else \n {\n this.script.notification(\"No Lore (Runes) skill found, cannot pass.\")\n failed = true;\n }\n\n\n let test = await this.actor.setupSkill(\"Art (Tattooing)\", {appendTitle :": ", fields : {difficulty : \"hard\"}})\n await Test.roll();\n if (Test.Échoué)\n {\n Échoué = true;\n }\n }\n else \n {\n this.script.notification(\"No Lore (Runes) Compétence found, Impossible de pass.\")\n Échoué = true;\n }\n\n\n let Test = await this.actor.setupSkill(\"Art (Tattooing)\", {appendTitle :",
")\n let configBlessings = await Promise.all((game.wfrp4e.config.godBlessings[god.toLowerCase()] || []).map(fromUuid));\n if (god ==": ")\n let configBlessings = await Promise.tout((game.wfrp4e.config.godBlessings[god.toLowerCase()] || []).map(fromUuid));\n if (god ==",
"))\n await this.actor.addEffectItems(blessings.map(i => i.uuid), this.effect)\n }\n else\n {\n this.script.scriptNotification(`Impossible de trouver des Bénédictions associées à ${god}.`)\n }\n if (this.item.name.includes(": "))\n await this.actor.addEffectItems(blessings.map(i => i.uuid), this.effet)\n }\n else\n {\n this.script.scriptNotification(`Impossible de trouver des Bénédictions associées à ${god}.`)\n }\n if (this.item.name.includes(",
", group)})\n\tthis.effect.updateSource({name : this.effect.name + ` (${group})`})\n}\n\n\ndata.name = data.name.replace(": ", group)})\n\tthis.effet.updateSource({name : this.effet.name + ` (${group})`})\n}\n\n\ndata.name = data.name.replace(",
", \"system.tests.value\" : this.item.system.tests.value.replace(\"Group\", group)})\n\tthis.effect.updateSource({name : this.effect.name +": ", \"system.Tests.value\" : this.item.system.Tests.value.replace(\"Group\", group)})\n\tthis.effet.updateSource({name : this.effet.name +",
"attrait du Léviathan.`);\n loreTest.renderRollCard();\n return;\n }\n}\n\nlet test = await actor.setupSkill(": "attrait du Léviathan.`);\n loreTest.renderRollCard();\n return;\n }\n}\n\nlet Test = await actor.setupSkill(",
", {\n appendTitle: ` ${this.effect.name}`,\n skipTargets: true,\n fields: {difficulty:": ", {\n appendTitle: ` ${this.effet.name}`,\n skipTargets: true,\n fields: {difficulty:",
");\n loreTest.renderRollCard();\n return;\n }\n}\n\nlet test = await actor.setupSkill('Calme', {\n appendTitle:": ");\n loreTest.renderRollCard();\n return;\n }\n}\n\nlet Test = await actor.setupSkill('Calme', {\n appendTitle:",
", this.effect.name)\n\nif (characteristics.has(": ", this.effet.name)\n\nif (characteristics.has(",
", availableChoices);\n\n if (!result) return;\n\n characteristics.add(result);\n}\n\nthis.effect.setFlag(": ", availableChoices);\n\n if (!result) return;\n\n characteristics.add(result);\n}\n\nthis.effet.setFlag(",
").roll({allowInteractive : false});\nlet regen = regenRoll.total;\n\nif (wounds.value >= wounds.max)\n return\n\nif (wounds.value > 0) \n{\n wounds.value += regen\n if (wounds.value > wounds.max)\n {\n wounds.value = wounds.max\n }\n message += `<b>${this.actor.name}</b> regains ${regen} Wounds.`\n\n if (regen == 10)\n {\n message += `<br>Additionally, they regenerate a Critical Wound.`\n }\n}\nelse if (regen >= 8) \n{\n message += `<b>${this.actor.name}</b> rolled a ${regen} and regains 1 Wound.`\n wounds.value += 1\n if (regen == 10)\n {\n message += `<br>Additionally, they regenerate a Critical Wound.`\n }\n}\nelse \n{\n message += `<b>${this.actor.name}</b> Régénération roll of ${regen} - No effect.`\n}\n\nawait this.actor.update({": ").roll({allowInteractive : false});\nlet regen = regenRoll.total;\n\nif (Blessures.value >= Blessures.max)\n return\n\nif (Blessures.value > 0) \n{\n Blessures.value += regen\n if (Blessures.value > Blessures.max)\n {\n Blessures.value = Blessures.max\n }\n message += `<b>${this.actor.name}</b> regains ${regen} Blessures.`\n\n if (regen == 10)\n {\n message += `<br>Additionally, they regenerate a Critique Wound.`\n }\n}\nelse if (regen >= 8) \n{\n message += `<b>${this.actor.name}</b> rolled a ${regen} and regains 1 Wound.`\n Blessures.value += 1\n if (regen == 10)\n {\n message += `<br>Additionally, they regenerate a Critique Wound.`\n }\n}\nelse \n{\n message += `<b>${this.actor.name}</b> Régénération roll of ${regen} - No effet.`\n}\n\nawait this.actor.update({",
": wounds })\nthis.script.message(message, { whisper: ChatMessage.getWhisperRecipients(": ": Blessures })\nthis.script.message(message, { whisper: ChatMessage.getWhisperRecipients(",
"wounds.value += 1\n if (regen == 10)\n {\n message +=": "Blessures.value += 1\n if (regen == 10)\n {\n message +=",
",\n value: /^((?!\\().)*$/gm, // Remove all spells with parentheses (all arcane spells spells)\n regex: true\n },\n {\n property :": ",\n value: /^((?!\\().)*$/gm, // Remove tout spells with parentheses (tout arcane spells spells)\n regex: true\n },\n {\n property :",
"+ this.effect.name\nawait this.actor.update(updateObj)\nthis.actor.createEmbeddedDocuments(": "+ this.effet.name\nawait this.actor.update(updateObj)\nthis.actor.createEmbeddedDocuments(",
"></i> Critical</a>`;\n \n const test = await this.actor.setupSkill(game.i18n.localize(": "></i> Critique</a>`;\n \n const Test = await this.actor.setupSkill(game.i18n.localize(",
"), {\n skipTargets: true,\n appendTitle: ` - ${this.effect.name}`,\n fields: {difficulty:": "), {\n skipTargets: true,\n appendTitle: ` - ${this.effet.name}`,\n fields: {difficulty:",
"<a data-action=\"clickTable\" class=\"action-link critical\" data-table=\"crithead\" data-modifier=\"0\"><i class=\"fas fa-list\"></i> Critical</a>": "<a data-action=\"clickTable\" class=\"action-link Critique\" data-table=\"crithead\" data-modifier=\"0\"><i class=\"fas fa-list\"></i> Critique</a>",
"Must provide a Master Rune": "Doit fournir une Rune Maîtresse",
", title : this.effect.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && item.system.master, onError: \"Must provide a Master Rune\"});\n }\n }\n else \n {\n rune = await DragDialog.create({text :": ", title : this.effet.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && item.system.master, onError: \"Doit provide a Master Rune\"});\n }\n }\n else \n {\n rune = await DragDialog.create({text :",
", title : this.effect.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && item.system.master, onError: \"Must provide a Master Rune\"});\n }\n}\nelse \n{\n rune = await DragDialog.create({text :": ", title : this.effet.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && item.system.master, onError: \"Doit provide a Master Rune\"});\n }\n}\nelse \n{\n rune = await DragDialog.create({text :",
", title : this.effect.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && item.system.master, onError: \"Must provide a Master Rune\"});\n}\n\nthis.actor.addEffectItems(rune.uuid, this.effect)\n\nlet talents = this.actor.itemTags.talent.filter(i => i.baseName == this.item.baseName);\nlet xpCost = talents.length * 100\n\nif (this.actor.type == \"character\" && (await foundry.applications.api.DialogV2.confirm({window: {title: this.effect.name}, content:": ", title : this.effet.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && item.system.master, onError: \"Doit provide a Master Rune\"});\n}\n\nthis.actor.addEffectItems(rune.uuid, this.effet)\n\nlet talents = this.actor.itemTags.talent.filter(i => i.baseName == this.item.baseName);\nlet xpCost = talents.length * 100\n\nif (this.actor.type == \"character\" && (await foundry.applications.api.DialogV2.confirm({window: {title: this.effet.name}, content:",
"Must provide a Rune (non-Master)": "Doit fournir une Rune (non-Maîtresse)",
", title : this.effect.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && !item.system.master, onError: \"Must provide a Rune (non-Master)\"});\n }\n }\n else \n {\n rune = await DragDialog.create({text :": ", title : this.effet.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && !item.system.master, onError: \"Doit provide a Rune (non-Master)\"});\n }\n }\n else \n {\n rune = await DragDialog.create({text :",
", title : this.effect.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && !item.system.master, onError: \"Must provide a Rune (non-Master)\"});\n }\n}\nelse \n{\n rune = await DragDialog.create({text :": ", title : this.effet.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && !item.system.master, onError: \"Doit provide a Rune (non-Master)\"});\n }\n}\nelse \n{\n rune = await DragDialog.create({text :",
", title : this.effect.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && !item.system.master, onError: \"Must provide a Rune (non-Master)\"});\n}\n\nthis.actor.addEffectItems(rune.uuid, this.effect)\n\nlet talents = this.actor.itemTags.talent.filter(i => i.baseName == this.item.baseName);\nlet xpCost = talents.length * 100\n\nif (this.actor.type == \"character\" && (await foundry.applications.api.DialogV2.confirm({window: {title: this.effect.name}, content:": ", title : this.effet.name, filter: (item) => item.type == \"wfrp4e-dwarfs.rune\" && !item.system.master, onError: \"Doit provide a Rune (non-Master)\"});\n}\n\nthis.actor.addEffectItems(rune.uuid, this.effet)\n\nlet talents = this.actor.itemTags.talent.filter(i => i.baseName == this.item.baseName);\nlet xpCost = talents.length * 100\n\nif (this.actor.type == \"character\" && (await foundry.applications.api.DialogV2.confirm({window: {title: this.effet.name}, content:",
", title : this.effect.name});\n if (value)\n {\n name = `${name.split(": ", title : this.effet.name});\n if (value)\n {\n name = `${name.split(",
")[0].trim()} (${value})`\n tests = this.item.system.tests.value.replace(": ")[0].trim()} (${value})`\n Tests = this.item.system.Tests.value.replace(",
"}\n else\n {\n let value = await ValueDialog.create({text : \"Entrez le Groupe d'Étiquette\", title : this.effect.name});\n if (value)\n {\n name =": "}\n else\n {\n let value = await ValueDialog.create({text : \"Entrez le Groupe d'Étiquette\", title : this.effet.name});\n if (value)\n {\n name =",
");\n}\n\nif (this.actor.items.find(i => i.type === \"extendedTest\" && i.name === this.effect.name)) {\n this.script.message(": ");\n}\n\nif (this.actor.items.find(i => i.type === \"extendedTest\" && i.name === this.effet.name)) {\n this.script.message(",
");\n\n return;\n}\n\nconst extendedTestData = {\n name: this.effect.name,\n type: \"extendedTest\",\n img: this.effect.img,\n system: {\n SL: {\n current: 0,\n target: target.actor.system.status.wounds.value\n },\n test: {\n value: 'Strength'\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: \"challenging\"\n }\n }\n};\n\nconst extendedTests = await this.actor.createEmbeddedDocuments(\"Item\", [extendedTestData], {fromEffect: this.effect.id});\nconst extendedTest = extendedTests[0];\n\nthis.script.message(": ");\n\n return;\n}\n\nconst extendedTestData = {\n name: this.effet.name,\n type: \"extendedTest\",\n img: this.effet.img,\n system: {\n SL: {\n current: 0,\n target: target.actor.system.status.Blessures.value\n },\n Test: {\n value: 'Strength'\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: \"challenging\"\n }\n }\n};\n\nconst extendedTests = await this.actor.createEmbeddedDocuments(\"Item\", [extendedTestData], {fromEffect: this.effet.id});\nconst extendedTest = extendedTests[0];\n\nthis.script.message(",
");\n\nlet effect = {\n name: extendedTest.name,\n img: extendedTest.img,\n system: {\n transferData : {\n type: \"document\",\n documentType: \"Item\"\n },\n scriptData: [\n {\n label: extendedTest.name,\n script:": ");\n\nlet effet = {\n name: extendedTest.name,\n img: extendedTest.img,\n system: {\n transferData : {\n type: \"document\",\n documentType: \"Item\"\n },\n scriptData: [\n {\n label: extendedTest.name,\n script:",
");\n await effect.delete();": ");\n await effet.delete();",
", {\n skipTargets: true, \n appendTitle: ` — ${this.effect.name}`, \n fields: {\n difficulty:": ", {\n skipTargets: true, \n appendTitle: ` — ${this.effet.name}`, \n fields: {\n difficulty:",
",\n }\n});\n\nawait test.roll();\nif (test.failure) {\n await this.actor.addCondition(": ",\n }\n});\n\nawait Test.roll();\nif (Test.failure) {\n await this.actor.addCondition(",
"}. Do you want to roll d10?</p>\n <p>16: add +1 SL</p>\n <p>710: lose all accrued SLs and perform next Test at 1 SL</p>\n </div>\n`;\n const choice = await foundry.applications.api.DialogV2.confirm({\n yes: {label:": "}. Do you want to roll d10?</p>\n <p>16: add +1 SL</p>\n <p>710: lose tout accrued SLs and perform next Test at 1 SL</p>\n </div>\n`;\n const choice = await foundry.applications.api.DialogV2.confirm({\n yes: {label:",
");\n await roll.toMessage({flavor: this.effect.name});\n\n if (roll.total >= 7) {\n SL = -1;\n break;\n }\n\n SL++;\n} while (true);\n\nthis.effect.setFlag(": ");\n await roll.toMessage({flavor: this.effet.name});\n\n if (roll.total >= 7) {\n SL = -1;\n break;\n }\n\n SL++;\n} while (true);\n\nthis.effet.setFlag(",
", { appendTitle: ` - ${this.effect.name}`, fields: { difficulty:": ", { appendTitle: ` - ${this.effet.name}`, fields: { difficulty:",
"} })\nawait test.roll();\n\nif (test.failed) \n{\n let ageAdded = Math.ceil(CONFIG.Dice.randomUniform() * 10) + Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let ws = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let bs = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let s = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let t = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let ag = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let dex = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n\n let currentAge = parseInt(this.actor.system.details.age.value)\n\n let inline = `<a class=": "} })\nawait Test.roll();\n\nif (Test.Échoué) \n{\n let ageAdded = Math.ceil(CONFIG.Dice.randomUniform() * 10) + Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let ws = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let bs = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let s = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let t = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let ag = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let dex = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n\n let currentAge = parseInt(this.actor.system.details.age.value)\n\n let inline = `<a class=",
", fields: { difficulty: \"challenging\" } })\nawait test.roll();\n\nif (test.failed) \n{\n let ageAdded = Math.ceil(CONFIG.Dice.randomUniform() * 10) + Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let ws = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let bs = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let s = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let t = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let ag = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let dex = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n\n let currentAge = parseInt(this.actor.system.details.age.value)\n\n let inline =": ", fields: { difficulty: \"challenging\" } })\nawait Test.roll();\n\nif (Test.Échoué) \n{\n let ageAdded = Math.ceil(CONFIG.Dice.randomUniform() * 10) + Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let ws = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let bs = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let s = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let t = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let ag = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n let dex = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n\n let currentAge = parseInt(this.actor.system.details.age.value)\n\n let inline =",
"Choose Group": "Choisir Group",
", this.actor).roll()\n\nroll.toMessage(this.script.getChatData({flavor : `${this.effect.name} (Durée)`}));\n\nthis.effect.updateSource({": ", this.actor).roll()\n\nroll.toMessage(this.script.getChatData({flavor : `${this.effet.name} (Durée)`}));\n\nthis.effet.updateSource({",
",\n {whisper: ChatMessage.getWhisperRecipients(\"GM\")})\n}\n\n// Notify of lingering effects when mask is removed\nelse if (!args.equipped)\n{\n await this.item.effects.contents[0].delete();\n await this.item.update({name : this.item.name += \" (Utilisé)\"})\n this.script.message(": ",\n {whisper: ChatMessage.getWhisperRecipients(\"GM\")})\n}\n\n// Notify of lingering effects when mask is Retiré\nelse if (!args.equipped)\n{\n await this.item.effects.contents[0].delete();\n await this.item.update({name : this.item.name += \" (Utilisé)\"})\n this.script.message(",
",\n {whisper: ChatMessage.getWhisperRecipients(\"GM\")})\n}\n\n// Notify of lingering effects when mask is removed\nelse if (!args.equipped)\n{\n await this.item.effects.contents[0].delete();\n await this.item.update({name : this.item.name += \" (Used)\"})\n this.script.message(": ",\n {whisper: ChatMessage.getWhisperRecipients(\"GM\")})\n}\n\n// Notify of lingering effects when mask is Retiré\nelse if (!args.equipped)\n{\n await this.item.effects.contents[0].delete();\n await this.item.update({name : this.item.name += \" (Used)\"})\n this.script.message(",
"}\n});\n\nawait test.roll();\n\nif (test.failed) {\n await this.actor.addEffectItems(bloodyFluxUUID, this.effect);\n} else {\n const SL = test.result.SL;\n const heal = 1 + SL;\n await this.actor.modifyWounds(heal);\n this.script.message(": "}\n});\n\nawait Test.roll();\n\nif (Test.Échoué) {\n await this.actor.addEffectItems(bloodyFluxUUID, this.effet);\n} else {\n const SL = Test.result.SL;\n const heal = 1 + SL;\n await this.actor.modifyWounds(heal);\n this.script.message(",
"You must control an Actor capable of performing a Strength Test": "You Doit control an Actor capable of performing a Strength Test",
"}\n});\n\nawait test.roll();\nif (test.succeeded) {\n let SL = parseInt(test.result.SL);\n let name = this.effect.name.replace(/\\d+/, rating => parseInt(rating) - SL);\n await this.effect.update({name});\n}\n\nlet rating = parseInt(this.effect.name.match(/\\d+/)?.[0]);\nif (rating <= 1) {\n const scriptData = this.effect.system.scriptData\n scriptData[2].trigger = '';\n await this.effect.update({disabled: true,": "}\n});\n\nawait Test.roll();\nif (Test.succeeded) {\n let SL = parseInt(Test.result.SL);\n let name = this.effet.name.replace(/\\d+/, rating => parseInt(rating) - SL);\n await this.effet.update({name});\n}\n\nlet rating = parseInt(this.effet.name.match(/\\d+/)?.[0]);\nif (rating <= 1) {\n const scriptData = this.effet.system.scriptData\n scriptData[2].trigger = '';\n await this.effet.update({disabled: true,",
", {appendTitle : ` - ${this.effect.name}`, fields : {difficulty :": ", {appendTitle : ` - ${this.effet.name}`, fields : {difficulty :",
"}});\n\nawait test.roll();\n\nif (test.failed)\n{\n\n if (test.isCriticalFumble ==": "}});\n\nawait Test.roll();\n\nif (Test.Échoué)\n{\n\n if (Test.isCriticalFumble ==",
", fields : {difficulty : \"difficult\"}});\n\nawait test.roll();\n\nif (test.failed)\n{\n\n if (test.isCriticalFumble == \"fumble\")\n {\n\t return this.script.message(": ", fields : {difficulty : \"difficult\"}});\n\nawait Test.roll();\n\nif (Test.Échoué)\n{\n\n if (Test.isCriticalFumble == \"fumble\")\n {\n\t return this.script.message(",
");\n loreTest.renderRollCard();\n return;\n } \n}\n\nlet test = await actor.setupSkill('Calme', {\n appendTitle:": ");\n loreTest.renderRollCard();\n return;\n } \n}\n\nlet Test = await actor.setupSkill('Calme', {\n appendTitle:",
", {skipTargets: true, appendTitle : ` - Used ${this.effect.name}`, fields: { difficulty:": ", {skipTargets: true, appendTitle : ` - Used ${this.effet.name}`, fields: { difficulty:",
"} }).then(async test => {\n await test.roll()\n if (test.failed) \n {\n let toughnessLost = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n this.actor.update({": "} }).then(async Test => {\n await Test.roll()\n if (Test.Échoué) \n {\n let toughnessLost = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n this.actor.update({",
", fields: { difficulty: \"difficult\" } }).then(async test => {\n await test.roll()\n if (test.failed) \n {\n let toughnessLost = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n this.actor.update({ \"system.characteristics.t.initial\": this.actor.characteristics.t.initial - toughnessLost })\n this.script.message(": ", fields: { difficulty: \"difficult\" } }).then(async Test => {\n await Test.roll()\n if (Test.Échoué) \n {\n let toughnessLost = Math.ceil(CONFIG.Dice.randomUniform() * 10)\n this.actor.update({ \"system.characteristics.t.initial\": this.actor.characteristics.t.initial - toughnessLost })\n this.script.message(",
": 0\n}\nlet skills = []\nlet skillAdvancements = []\nlet talents = []\nlet trappings = []\nlet items = []\n\nlet updateObj = this.actor.toObject();\n\nfor (let ch in characteristics)\n{\n updateObj.system.characteristics[ch].modifier += characteristics[ch];\n}\n\nfor (let index = 0; index < skills.length; index++)\n{\n let skill = skills[index]\n let skillItem;\n skillItem = updateObj.items.find(i => i.name == skill && i.type ==": ": 0\n}\nlet skills = []\nlet skillAdvancements = []\nlet talents = []\nlet trappings = []\nlet items = []\n\nlet updateObj = this.actor.toObject();\n\nfor (let ch in characteristics)\n{\n updateObj.system.characteristics[ch].modifier += characteristics[ch];\n}\n\nfor (let index = 0; index < skills.length; index++)\n{\n let Compétence = skills[index]\n let skillItem;\n skillItem = updateObj.items.find(i => i.name == Compétence && i.type ==",
")\n if (skillItem)\n skillItem.system.advances.value += skillAdvancements[index]\n else\n {\n skillItem = await game.wfrp4e.utility.findSkill(skill)\n skillItem = skillItem.toObject();\n skillItem.system.advances.value = skillAdvancements[index];\n items.push(skillItem);\n }\n}\n\nfor (let talent of talents)\n{\n let talentItem = await game.wfrp4e.utility.findTalent(talent)\n if (talentItem)\n {\n items.push(talentItem.toObject());\n }\n else\n {\n ui.notifications.warn(`Impossible de trouver ${talent}`, {permanent : true})\n }\n}\n\nfor (let trapping of trappings)\n{\n let trappingItem = await game.wfrp4e.utility.findItem(trapping)\n if (trappingItem)\n {\n trappingItem = trappingItem.toObject()\n\n trappingItem.system.equipped.value = true;\n\n items.push(trappingItem);\n }\n else\n {\n ui.notifications.warn(`Impossible de trouver ${trapping}`, {permanent : true})\n }\n}\n\n\nawait this.actor.update(updateObj)\nthis.actor.createEmbeddedDocuments(": ")\n if (skillItem)\n skillItem.system.advances.value += skillAdvancements[index]\n else\n {\n skillItem = await game.wfrp4e.utility.findSkill(Compétence)\n skillItem = skillItem.toObject();\n skillItem.system.advances.value = skillAdvancements[index];\n items.push(skillItem);\n }\n}\n\nfor (let talent of talents)\n{\n let talentItem = await game.wfrp4e.utility.findTalent(talent)\n if (talentItem)\n {\n items.push(talentItem.toObject());\n }\n else\n {\n ui.notifications.warn(`Impossible de trouver ${talent}`, {permanent : true})\n }\n}\n\nfor (let trapping of trappings)\n{\n let trappingItem = await game.wfrp4e.utility.findItem(trapping)\n if (trappingItem)\n {\n trappingItem = trappingItem.toObject()\n\n trappingItem.system.equipped.value = true;\n\n items.push(trappingItem);\n }\n else\n {\n ui.notifications.warn(`Impossible de trouver ${trapping}`, {permanent : true})\n }\n}\n\n\nawait this.actor.update(updateObj)\nthis.actor.createEmbeddedDocuments(",
"<p>\n Select your choice\n </p>\n <ol>\n <li>Melee (Basic)</li>\n <li>Melee (Two-Handed)</li>\n </ol>": "<p>\n Sélectionner your choice\n </p>\n <ol>\n <li>Melee (Basic)</li>\n <li>Melee (Two-Handed)</li>\n </ol>",
"Choose Qualities to add": "Choisir Qualities to add",
"Choose Flaws to remove": "Choisir Flaws to remove",
", {skipTargets: true, appendTitle : ` - ${this.effect.name}`})\nawait test.roll();\n\n\nlet opposedResult = test.opposedMessages[0]?.system.opposedHandler?.resultMessage?.system.opposedTest?.result\n\nif (opposedResult?.winner ==": ", {skipTargets: true, appendTitle : ` - ${this.effet.name}`})\nawait Test.roll();\n\n\nlet opposedResult = Test.opposedMessages[0]?.system.opposedHandler?.resultMessage?.system.opposedTest?.result\n\nif (opposedResult?.winner ==",
"})\nawait test.roll();\n\n\nlet opposedResult = test.opposedMessages[0]?.system.opposedHandler?.resultMessage?.system.opposedTest?.result\n\nif (opposedResult?.winner == \"attacker\")\n{\n let spells = this.actor.itemTypes.spell;\n if (spells.length)\n {\n let chosen = spells[Math.floor(CONFIG.Dice.randomUniform() * spells.length)]\n this.script.message(": "})\nawait Test.roll();\n\n\nlet opposedResult = Test.opposedMessages[0]?.system.opposedHandler?.resultMessage?.system.opposedTest?.result\n\nif (opposedResult?.winner == \"attacker\")\n{\n let spells = this.actor.itemTypes.spell;\n if (spells.length)\n {\n let chosen = spells[Math.floor(CONFIG.Dice.randomUniform() * spells.length)]\n this.script.message(",
"<p>Damage all Items carried?</p>": "<p>Damage tout Items carried?</p>",
"Choose Optional Traits": "Choisir Optional Traits",
"You must target exactly one Boat.": "You Doit target exactly one Boat.",
": []});\n msg += `<p>${item.name} loses all Qualities</p>`\n }\n else \n {\n msg += `<p>${item.name} crumbles into dust!</p>` \n await item.update({name : item.name +": ": []});\n msg += `<p>${item.name} loses tout Qualities</p>`\n }\n else \n {\n msg += `<p>${item.name} crumbles into dust!</p>` \n await item.update({name : item.name +",
"}});\nawait test.roll();\nCorruptionMessageModel.createCorruptionMessage(": "}});\nawait Test.roll();\nCorruptionMessageModel.createCorruptionMessage(",
", this.script.getChatData())\n\nif (test.failed)\n{\n this.actor.addCondition(": ", this.script.getChatData())\n\nif (Test.Échoué)\n{\n this.actor.addCondition(",
")\n\nlet value = hitloc.result\nlet desc = hitloc.description\n\nthis.effect.updateSource({": ")\n\nlet value = hitloc.result\nlet desc = hitloc.description\n\nthis.effet.updateSource({",
"EFFECT.CreatureBackInWater": "Effet.CreatureBackInWater",
"EFFECT.CreatureOutOfWater": "Effet.CreatureOutOfWater",
": 5\n }\n let items = []\n\n let updateObj = this.actor.toObject();\n\n let talents = (await Promise.all([game.wfrp4e.tables.rollTable(": ": 5\n }\n let items = []\n\n let updateObj = this.actor.toObject();\n\n let talents = (await Promise.tout([game.wfrp4e.tables.rollTable(",
";\nconst test = 'Métier (Charpentier)';\nconst difficulty = 'challenging';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Métier (Charpentier)';\nconst difficulty = 'challenging';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
"}\n});\n\nawait test.roll();\n\nif (test.failed) {\n args.actor.addCondition(\"poisoned\");\n const speaker = ChatMessage.getSpeaker({actor: args.actor});\n this.script.message(": "}\n});\n\nawait Test.roll();\n\nif (Test.Échoué) {\n args.actor.addCondition(\"poisoned\");\n const speaker = ChatMessage.getSpeaker({actor: args.actor});\n this.script.message(",
")})\n if (args.test.failed && this.actor.type ==": ")})\n if (args.Test.Échoué && this.actor.type ==",
"Set Liquid Fortification effect duration to 1 hour.": "Set Liquid Fortification effet duration to 1 hour.",
"Reset Liquid Fortification effect duration to 1 hour.": "Reset Liquid Fortification effet duration to 1 hour.",
"Choose Symptoms": "Choisir Symptoms",
"&& args.test.failed && args.test.result.SL <= -3)\n{\n this.script.notification(": "&& args.Test.Échoué && args.Test.result.SL <= -3)\n{\n this.script.notification(",
", {skipTargets: true, appendTitle : ` - ${this.effect.name}`})\nawait test.roll();\nif (test.succeeded)\n{\n this.script.message(": ", {skipTargets: true, appendTitle : ` - ${this.effet.name}`})\nawait Test.roll();\nif (Test.succeeded)\n{\n this.script.message(",
"This Actor does not know any runes.": "This Actor does not know tout runes.",
") return;\n\nconst SL = args.opposedTest.data.opposeResult.differenceSL;\n\nconst targetId = this.effect.getFlag(": ") return;\n\nconst SL = args.opposedTest.data.opposeResult.differenceSL;\n\nconst targetId = this.effet.getFlag(",
");\n return await this.effect.delete();\n}\n\nif (SL > 0) {\n args.opposedTest.data.opposeResult.other.push(": ");\n return await this.effet.delete();\n}\n\nif (SL > 0) {\n args.opposedTest.data.opposeResult.other.push(",
";\nconst test = 'Métier (Charpentier)';\nconst difficulty = 'difficult';\nconst target = 20;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Métier (Charpentier)';\nconst difficulty = 'difficult';\nconst target = 20;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
"All Effects have been used. Reset to select them again": "Tout Effects have been used. Reset to Sélectionner them again",
"Choose Power to Gain": "Choisir Power to Gain",
";\nconst test = 'Trade (Carpentry)';\nconst difficulty = 'easy';\nconst target = 20;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Trade (Carpentry)';\nconst difficulty = 'easy';\nconst target = 20;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
";\nconst test = 'Voile';\nconst difficulty = 'easy';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Voile';\nconst difficulty = 'easy';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
";\nconst test = 'Trade (Carpentry)';\nconst difficulty = 'hard';\nconst target = 30;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Trade (Carpentry)';\nconst difficulty = 'hard';\nconst target = 30;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
";\nconst test = 'Voile';\nconst difficulty = 'average';\nconst target = 30;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Voile';\nconst difficulty = 'average';\nconst target = 30;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
";\nconst test = 'Métier (Ingénieur)';\nconst difficulty = 'easy';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Métier (Ingénieur)';\nconst difficulty = 'easy';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
";\nconst test = 'Métier (Charpentier)';\nconst difficulty = 'difficult';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Métier (Charpentier)';\nconst difficulty = 'difficult';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
";\nconst test = 'Métier (Charpentier)';\nconst difficulty = 'hard';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Métier (Charpentier)';\nconst difficulty = 'hard';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
";\nconst test = 'Strength';\nconst difficulty = 'average';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Strength';\nconst difficulty = 'average';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
", {skipTargets: true, appendTitle : ` - ${this.effect.name}`, context: { failure:": ", {skipTargets: true, appendTitle : ` - ${this.effet.name}`, context: { failure:",
"}\n});\n\nawait test.roll();\n\nif (test.failed) {\n const SL = Number(test.result.SL);\n this.script.message(": "}\n});\n\nawait Test.roll();\n\nif (Test.Échoué) {\n const SL = Number(Test.result.SL);\n this.script.message(",
": locations})\nthis.effect.updateSource({": ": locations})\nthis.effet.updateSource({",
";\nconst test = 'Métier (Charpentier)';\nconst difficulty = 'challenging';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Métier (Charpentier)';\nconst difficulty = 'challenging';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
"s name\nawait effect.update({\n name,\n \"system.scriptData\": scriptData\n});\n\n\n// update Trait": "s name\nawait effet.update({\n name,\n \"system.scriptData\": scriptData\n});\n\n\n// update Trait",
";\nconst test = 'Trade (Tailor)';\nconst difficulty = 'difficult';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Trade (Tailor)';\nconst difficulty = 'difficult';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
"]\n }\n]\n\nlet arcane = await ItemDialog.createFromFilters(filters, 12, {title : this.effect.name, text :": "]\n }\n]\n\nlet arcane = await ItemDialog.createFromFilters(filters, 12, {title : this.effet.name, text :",
";\nconst test = 'Trade (Carpentry)';\nconst difficulty = 'easy';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Trade (Carpentry)';\nconst difficulty = 'easy';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
";\nconst test = 'Trade (Carpentry)';\nconst difficulty = 'difficult';\nconst target = 30;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Trade (Carpentry)';\nconst difficulty = 'difficult';\nconst target = 30;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
"}})\nawait test.roll();\n\nif (!test.succeeded)\n{\n\tthis.actor.addCondition(": "}})\nawait Test.roll();\n\nif (!Test.succeeded)\n{\n\tthis.actor.addCondition(",
"}});\nawait test.roll();\n\nif (test.failed)\n{\n await this.actor.addCondition(": "}});\nawait Test.roll();\n\nif (Test.Échoué)\n{\n await this.actor.addCondition(",
";\nconst test = 'Strength';\nconst difficulty = 'easy';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Strength';\nconst difficulty = 'easy';\nconst target = 10;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
"; // Put path to token image here, inbetween the quotation marks\nif (tokenImg)\n{\n if (this.effect.getFlag(": "; // Put path to token image here, inbetween the quotation marks\nif (tokenImg)\n{\n if (this.effet.getFlag(",
";\n\t\t\targs.test.result.description =": ";\n\t\t\targs.Test.result.description =",
"}\n\t\targs.test.result.outcome =": "}\n\t\targs.Test.result.outcome =",
"Failed Trade (Engineering)": "Échoué Trade (Engineering)",
";\nconst test = 'Trade (Tailor)';\nconst difficulty = 'easy';\nconst target = 20;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Trade (Tailor)';\nconst difficulty = 'easy';\nconst target = 20;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
") \n{\n if (args.test.failed)\n {\n let item = await fromUuid(": ") \n{\n if (args.Test.Échoué)\n {\n let item = await fromUuid(",
";\nconst test = 'Trade (Carpentry)';\nconst difficulty = 'challenging';\nconst target = 20;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Trade (Carpentry)';\nconst difficulty = 'challenging';\nconst target = 20;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
";\nconst test = 'Métier (Charpentier)';\nconst difficulty = 'difficult';\nconst target = 50;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Métier (Charpentier)';\nconst difficulty = 'difficult';\nconst target = 50;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
") \n{\n if (args.test.failed)\n {\n this.actor.createEmbeddedDocuments(": ") \n{\n if (args.Test.Échoué)\n {\n this.actor.createEmbeddedDocuments(",
"]])\n this.script.message(`Willpower Test failed, <b>${this.actor.prototypeToken.name}</b> gains @Condition[Malaise] for [[1d10]] hours`, {whisper: ChatMessage.getWhisperRecipients(": "]])\n this.script.message(`Willpower Test Échoué, <b>${this.actor.prototypeToken.name}</b> gains @Condition[Malaise] for [[1d10]] hours`, {whisper: ChatMessage.getWhisperRecipients(",
"}\n \n args.test.result.other.push (": "}\n \n args.Test.result.other.push (",
";\nconst test = 'Trade (Tailor)';\nconst difficulty = 'easy';\nconst target = 30;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Trade (Tailor)';\nconst difficulty = 'easy';\nconst target = 30;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
").map(i => `max(0, 1d10 - ${this.actor.system.characteristics.fel.bonus})`)\n\nlet test = new Roll(`${rolls.join(": ").map(i => `max(0, 1d10 - ${this.actor.system.characteristics.fel.bonus})`)\n\nlet Test = new Roll(`${rolls.join(",
")\n\nlet test = new Roll(": ")\n\nlet Test = new Roll(",
", {skipTargets: true, appendTitle : ` - ${this.effect.name}`, fields: { difficulty:": ", {skipTargets: true, appendTitle : ` - ${this.effet.name}`, fields: { difficulty:",
"} }).then(async test => {\n await test.roll();\n if (test.failed) \n {\n await args.actor.addCondition(": "} }).then(async Test => {\n await Test.roll();\n if (Test.Échoué) \n {\n await args.actor.addCondition(",
"}})\nawait test.roll();\nif (test.failed)\n{\n let roll = await new Roll(": "}})\nawait Test.roll();\nif (Test.Échoué)\n{\n let roll = await new Roll(",
";\nconst test = 'Métier (Charpentier)';\nconst difficulty = 'challenging';\nconst target = 20;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Métier (Charpentier)';\nconst difficulty = 'challenging';\nconst target = 20;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
"<p>Does the vessel have a magazine or any other kind of store for blackpowder?</p>": "<p>Does the vessel have a magazine or tout other kind of store for blackpowder?</p>",
";\nconst test = 'Voile';\nconst difficulty = 'challenging';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Voile';\nconst difficulty = 'challenging';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
";\nconst test = 'Métier (Charpentier)';\nconst difficulty = 'easy';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n test: {\n value: test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:": ";\nconst Test = 'Métier (Charpentier)';\nconst difficulty = 'easy';\nconst target = 40;\n\nconst extendedTestData = {\n name: this.item.name,\n type: \"extendedTest\",\n img: this.item.img,\n system: {\n SL: {\n current: 0,\n target: target\n },\n Test: {\n value: Test\n },\n completion: {\n value: \"remove\"\n },\n difficulty: {\n value: difficulty\n }\n },\n effects: [\n {\n name:",
"this.script.message(msg)\n\nlet test = await this.actor.setupSkill(\"Résistance\", {fields : {difficulty : \"difficult\"}, appendTitle :": "this.script.message(msg)\n\nlet Test = await this.actor.setupSkill(\"Résistance\", {fields : {difficulty : \"difficult\"}, appendTitle :",
"Choose Location": "Choisir Location",
"Hatred (All enemies)": "Hatred (Tout enemies)",
", {skipTargets: true, appendTitle : ` - ${this.effect.name}`});\nawait test.roll();\nif (test.failed)\n{\n this.actor.addCondition(": ", {skipTargets: true, appendTitle : ` - ${this.effet.name}`});\nawait Test.roll();\nif (Test.Échoué)\n{\n this.actor.addCondition(",
")\n this.actor.modifyWounds(-1 * value)\n await this.item.system.toggleEquip();\n args.test.addSL(value);\n args.test.preData.other.push(": ")\n this.actor.modifyWounds(-1 * value)\n await this.item.system.toggleEquip();\n args.Test.addSL(value);\n args.Test.preData.other.push(",
"<p>This Talent also extends to any metal object because of <strong>Metallic Affinity</strong></p>": "<p>This Talent also extends to tout metal object because of <strong>Metallic Affinity</strong></p>",
"Halve Damage? (Halves all damage other than fire, cold, and magic)": "Halve Damage? (Halves tout damage other than fire, cold, and magic)",
"Choose Limb": "Choisir Limb",
"Effect Used": "Effet Used",
"must make a <b>Willpower</b> Test or fall @Condition[Prone].": "Doit make a <b>Willpower</b> Test or fall @Condition[Prone].",
"Cannot remove Blinded condition.": "Impossible de remove Blinded condition.",
"Removed Fatigued": "Retiré Fatigued",
"})\nawait test.roll();\n\nif (test.failed) {\n \tthis.script.scriptMessage(await this.actor.applyBasicDamage(20, {suppressMsg: true}));\n this.script.scriptMessage(": "})\nawait Test.roll();\n\nif (Test.Échoué) {\n \tthis.script.scriptMessage(await this.actor.applyBasicDamage(20, {suppressMsg: true}));\n this.script.scriptMessage(",
"Cannot move or act this Turn": "Impossible de move or act this Turn",
"Select Wind": "Sélectionner Wind",
")\n bleeding.delete(); \n}\nelse \n{\n this.script.notification(": ")\n Saignement.delete(); \n}\nelse \n{\n this.script.notification(",
"Choose a Skill": "Choisir a Compétence",
") \n{\n if (args.test.failed)\n {\n this.actor.addSystemEffect(": ") \n{\n if (args.Test.Échoué)\n {\n this.actor.addSystemEffect(",
",\n}\n\nlet test = await args.actor.setupSkill(": ",\n}\n\nlet Test = await args.actor.setupSkill(",
"Cannot be Surprised": "Impossible de be Surprised",
", {skipTargets: true, appendTitle : ` - ${this.effect.name}`})\nawait test.roll();\nif (test.failed)\n{\n this.script.message(await game.wfrp4e.tables.formatChatRoll(": ", {skipTargets: true, appendTitle : ` - ${this.effet.name}`})\nawait Test.roll();\nif (Test.Échoué)\n{\n this.script.message(await game.wfrp4e.tables.formatChatRoll(",
"}, skipTargets: true, appendTitle : ` - ${this.effect.name}`})\nawait test.roll();\nif (test.failed)\n{\n\tlet stuns = Math.max(1, Math.abs(test.result.SL))\n\tthis.actor.addCondition(": "}, skipTargets: true, appendTitle : ` - ${this.effet.name}`})\nawait Test.roll();\nif (Test.Échoué)\n{\n\tlet stuns = Math.max(1, Math.abs(Test.result.SL))\n\tthis.actor.addCondition(",
")\n}\nelse \n{\n\tlet points = this.effect.sourceTest.result.overcast.usage.other.current;\n\tthis.actor.update({\"system.status.corruption.value\" : this.actor.system.status.corruption.value - points})\n\tthis.script.message(": ")\n}\nelse \n{\n\tlet points = this.effet.sourceTest.result.overcast.usage.other.current;\n\tthis.actor.update({\"system.status.corruption.value\" : this.actor.system.status.corruption.value - points})\n\tthis.script.message(",
"})\nawait test.roll();\nif (test.failed)\n{\n\n let myRoll = await new Roll(\"1d10\").roll({allowInteractive : false});\n let duration = myRoll.total\n this.actor.addSystemEffect(\"nausea\");\n this.script.scriptMessage(": "})\nawait Test.roll();\nif (Test.Échoué)\n{\n\n let myRoll = await new Roll(\"1d10\").roll({allowInteractive : false});\n let duration = myRoll.total\n this.actor.addSystemEffect(\"nausea\");\n this.script.scriptMessage(",
"Choose the Critical Wounds to heal (cannot reattach body parts)": "Choisir the Critique Blessures to heal (Impossible de reattach body parts)",
"Choose Taille": "Choisir Taille",
"Cannot attack targets that are not Entangled": "Impossible de attack targets that are not Entangled",
").pop())\n\n if (ones == 0) {\n ones = 10;\n }\n\n if (ones > SL) {\n args.test.data.result.SL =": ").pop())\n\n if (ones == 0) {\n ones = 10;\n }\n\n if (ones > SL) {\n args.Test.data.result.SL =",
"Gained 2 Bleeding Conditions": "Gained 2 Saignement Conditions",
"Choose a Disease": "Choisir a Disease",
"});\nawait test.roll();\nif (test.failed)\n{\n\tawait this.actor.addCondition(\"blinded\");\n}\n\nlet msg =": "});\nawait Test.roll();\nif (Test.Échoué)\n{\n\tawait this.actor.addCondition(\"blinded\");\n}\n\nlet msg =",
")\n }\n else if (this.actor.characteristics.int.bonus + Number(args.test.result.SL) < 0)\n {\n args.test.result.other.push(": ")\n }\n else if (this.actor.characteristics.int.bonus + Number(args.Test.result.SL) < 0)\n {\n args.Test.result.other.push(",
"});\nawait test.roll();\n\nif (test.failed) {\n this.actor.addCondition('stunned');\n}\n\nthis.script.scriptNotification(": "});\nawait Test.roll();\n\nif (Test.Échoué) {\n this.actor.addCondition('stunned');\n}\n\nthis.script.scriptNotification(",
"Added Fatigued": "Ajouté Fatigued",
"t use this.effect because it thinks it": "t use this.effet because it thinks it",
"Halve Damage? (Halves Damage from all fire)": "Halve Damage? (Halves Damage from tout fire)",
"Can only be applied to River Troll.": "Can only be Appliqué to River Troll.",
"+ parseInt(this.effect.sourceTest.result.SL)": "+ parseInt(this.effet.sourceTest.result.SL)",
")\nthis.item.setFlag(\"wfrp4e\", \"uses\", uses);\nif (uses >= 3)\n{\n\tthis.effect.update({\"system.transferData.type\" : \"other\"})\n\tthis.script.notification(": ")\nthis.item.setFlag(\"wfrp4e\", \"uses\", uses);\nif (uses >= 3)\n{\n\tthis.effet.update({\"system.transferData.type\" : \"other\"})\n\tthis.script.notification(",
"Wounds Healed": "Blessures Healed",
"Choose a Weapon": "Choisir a Weapon",
"ambre\";\n amberTalons.img = this.effect.img;\n amberTalons.system.damage.value = \"SB + WPB\"\n amberTalons.system.equipped = true;\n amberTalons.system.qualities.value.push({name : \"magical\"})\n amberTalons.effects.push({\n name : \"Serres d": "ambre\";\n amberTalons.img = this.effet.img;\n amberTalons.system.damage.value = \"SB + WPB\"\n amberTalons.system.equipped = true;\n amberTalons.system.qualities.value.push({name : \"magical\"})\n amberTalons.effects.push({\n name : \"Serres d",
")\n args.test.preData.reversal = {allowed: true, if:": ")\n args.Test.preData.reversal = {allowed: true, if:",
", {skipTargets: true, appendTitle : ` - ${this.effect.name}`, fields : {difficulty :": ", {skipTargets: true, appendTitle : ` - ${this.effet.name}`, fields : {difficulty :",
"}});\nawait test.roll();\nif (test.failed)\n{\n\tawait this.actor.addCondition(\"blinded\");\n}\n\nlet msg =": "}});\nawait Test.roll();\nif (Test.Échoué)\n{\n\tawait this.actor.addCondition(\"blinded\");\n}\n\nlet msg =",
"Can roll on the @Table[crithead]{Head Critical Hits} instead of the normal hit location": "Can roll on the @Table[crithead]{Head Critique Hits} instead of the normal hit location",
"hast)\",\n diff : {\n system : {\n advances : {\n value : 20\n }\n }\n }\n }\n]\n\nlet choice3 = [\n {\n type : \"skill\",\n name : \"Mêlée (Deux mains)\",\n diff : {\n system : {\n advances : {\n value : 20\n }\n }\n }\n }\n]\n\nlet choice = await foundry.applications.api.DialogV2.wait({\n window : {title : \"Choix\"},\n content :\n `<p>\n Sélectionnez votre choix\n </p>\n <ol>\n <li>Mêlée (Base)</li>\n <li>Mêlée (Arme d": "hast)\",\n diff : {\n system : {\n advances : {\n value : 20\n }\n }\n }\n }\n]\n\nlet choice3 = [\n {\n type : \"Compétence\",\n name : \"Mêlée (Deux mains)\",\n diff : {\n system : {\n advances : {\n value : 20\n }\n }\n }\n }\n]\n\nlet choice = await foundry.applications.api.DialogV2.wait({\n window : {title : \"Choix\"},\n content :\n `<p>\n Sélectionnez votre choix\n </p>\n <ol>\n <li>Mêlée (Base)</li>\n <li>Mêlée (Arme d",
"<b>Rune of Goblin Bane</b>: Impact Added": "<b>Rune of Goblin Bane</b>: Impact Ajouté",
"Enter Grim value": "Entrer Grim value",
", {skipTargets: true, appendTitle : ` - ${this.effect.name}`})\nawait test.roll();\n\n// Kind of insane but whatever\nlet opposedResult = test.opposedMessages[0]?.system.opposedHandler?.resultMessage?.system.opposedTest?.result\n\nreturn opposedResult?.winner ==": ", {skipTargets: true, appendTitle : ` - ${this.effet.name}`})\nawait Test.roll();\n\n// Kind of insane but whatever\nlet opposedResult = Test.opposedMessages[0]?.system.opposedHandler?.resultMessage?.system.opposedTest?.result\n\nreturn opposedResult?.winner ==",
"Cannot suffer Critical Wounds": "Impossible de suffer Critique Blessures",
"Rolled and applied mutations": "Rolled and Appliqué mutations",
")\n }\n else if (this.actor.characteristics.int.bonus + Number(args.test.result.SL) < 0)\n {\n args.test.result.other.push(": ")\n }\n else if (this.actor.characteristics.int.bonus + Number(args.Test.result.SL) < 0)\n {\n args.Test.result.other.push(",
").pop())\n\n if (ones > SL) {\n args.test.data.result.SL =": ").pop())\n\n if (ones > SL) {\n args.Test.data.result.SL =",
"Cannot be surprised": "Impossible de be surprised",
"Removed 1 Bleeding Condition": "Retiré 1 Saignement Condition",
"}, skipTargets: true, appendTitle : \" - \" + this.effect.name, fields : {difficulty : \"average\"}})\nawait test.roll();\n\n\nif (location && test.failed)\n{\n let dropped = this.item.system.weaponsAtLocation;\n\n if (dropped.length)\n {\n this.script.notification(": "}, skipTargets: true, appendTitle : \" - \" + this.effet.name, fields : {difficulty : \"average\"}})\nawait Test.roll();\n\n\nif (location && Test.Échoué)\n{\n let dropped = this.item.system.weaponsAtLocation;\n\n if (dropped.length)\n {\n this.script.notification(",
").pop())\n\n if (ones == 0) {\n ones = 10;\n }\n\n\n if (ones > SL) {\n args.test.data.result.SL =": ").pop())\n\n if (ones == 0) {\n ones = 10;\n }\n\n\n if (ones > SL) {\n args.Test.data.result.SL =",
", fields : {difficulty : \"difficult\", slBonus : -1 * this.effect.sourceTest.result.SL}})\nawait test.roll();\nif (test.succeeded)\n{\n\tthis.script.notification(": ", fields : {difficulty : \"difficult\", slBonus : -1 * this.effet.sourceTest.result.SL}})\nawait Test.roll();\nif (Test.succeeded)\n{\n\tthis.script.notification(",
"Target has higher Willpower, no effect": "Target has higher Willpower, no effet",
"Choose a disease to heal (must be naturally occuring)": "Choisir a disease to heal (Doit be naturally occuring)",
"|| this.item.getFlag(": "|| this.item.getFlag(",
"Removed Broken": "Condition Brisé supprimée",
"Does not need to make Peur or Terror tests": "N'a pas besoin de faire de tests de Peur ou de Terreur",
",\"info\")\n}\n\nconst runesOfRestoration = this.item.effects.contents.filter(e => e.name == this.effect.name)\nconst restorationWounds = parseInt(runesOfRestoration.length * this.actor.system.characteristics.t.bonus)\n\nthis.actor.modifyWounds(restorationWounds)\nthis.script.message(": ",\"info\")\n}\n\nconst runesOfRestoration = this.item.effects.contents.filter(e => e.name == this.effet.name)\nconst restorationWounds = parseInt(runesOfRestoration.length * this.actor.system.characteristics.t.bonus)\n\nthis.actor.modifyWounds(restorationWounds)\nthis.script.message(",
"Automatically passes any": "Réussit automatiquement tout"
}

File diff suppressed because it is too large Load Diff

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

216
tools/find-english-texts.js Normal file
View 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 FR_DIR = path.join(__dirname, 'scripts');
// Mots anglais communs pour détecter du texte en anglais
const englishWords = [
'the', 'and', 'or', 'to', 'from', 'with', 'without', 'has', 'have', 'had',
'is', 'are', 'was', 'were', 'been', 'being', 'will', 'would', 'should',
'can', 'could', 'may', 'might', 'must', 'shall',
'this', 'that', 'these', 'those', 'what', 'which', 'who', 'when', 'where',
'not', 'all', 'any', 'each', 'every', 'some', 'other', 'another',
'gain', 'gained', 'lose', 'lost', 'add', 'added', 'remove', 'removed',
'take', 'taken', 'give', 'given', 'make', 'made', 'find', 'found',
'get', 'got', 'set', 'put', 'use', 'used', 'using',
'apply', 'applied', 'test', 'tested', 'roll', 'rolled',
'if', 'else', 'while', 'for', 'return', // Mots-clés JS à ignorer dans le code
'failed', 'passed', 'success', 'failure', 'check', 'checked',
'attack', 'damage', 'heal', 'healed', 'wound', 'wounded',
'spell', 'cast', 'casting', 'magic', 'effect', 'affected',
'target', 'targets', 'hit', 'miss', 'critical',
'weapon', 'armour', 'armor', 'shield', 'equipped',
'skill', 'talent', 'trait', 'ability', 'bonus',
'enter', 'choose', 'select', 'click', 'press',
'must', 'need', 'require', 'required', 'cannot', 'unable',
'ignore', 'ignored', 'prevent', 'prevented', 'resist', 'resisted',
'duration', 'permanent', 'temporary', 'condition', 'status',
'character', 'actor', 'creature', 'enemy', 'ally',
'during', 'until', 'after', 'before', 'when', 'while'
];
// Patterns de textes techniques à ignorer (clés, IDs, etc.)
const technicalPatterns = [
/^[a-z]+$/, // Tout en minuscules (probablement une clé)
/^[0-9]+$/, // Que des chiffres
/^[0-9a-f]{16}$/i, // Hash/ID
/^[a-z_]+$/, // snake_case (clé technique)
/^[a-z][a-zA-Z]+$/, // camelCase (clé technique)
/^\$\{.*\}$/, // Template literal variable
/^system\./, // Chemin système
/^wfrp4e/, // Préfixe système
/^@/, // Référence (ex: @Condition)
/^[A-Z]{2,}$/, // Acronymes courts
/^[\w-]{1,10}$/, // Clés courtes
];
// Fonction pour extraire les chaînes d'un fichier
function extractStrings(content) {
const strings = new Set();
// Patterns pour capturer les chaînes entre guillemets
// On évite les template literals complexes avec interpolation
const patterns = [
/"([^"]{3,})"/g, // Double quotes
/'([^']{3,})'/g, // Single quotes
];
patterns.forEach(pattern => {
const matches = content.matchAll(pattern);
for (const match of matches) {
const str = match[1].trim();
if (str.length > 2) {
strings.add(str);
}
}
});
// Pour les template literals, on cherche ceux sans interpolation ou simples
const backtickPattern = /`([^`\$]{3,})`/g;
const backtickMatches = content.matchAll(backtickPattern);
for (const match of backtickMatches) {
const str = match[1].trim();
if (str.length > 2) {
strings.add(str);
}
}
return Array.from(strings);
}
// Fonction pour détecter si une chaîne est probablement du texte en anglais
function isEnglishText(str) {
// Ignorer les patterns techniques
for (const pattern of technicalPatterns) {
if (pattern.test(str)) {
return false;
}
}
// Ignorer les chaînes trop courtes
if (str.length < 3) {
return false;
}
// Convertir en minuscules pour la comparaison
const lowerStr = str.toLowerCase();
// Vérifier la présence de mots anglais
const words = lowerStr.split(/\W+/);
const englishWordCount = words.filter(word =>
englishWords.includes(word) && word.length > 1
).length;
// Si au moins 1 mot anglais significatif est trouvé, c'est probablement de l'anglais
if (englishWordCount > 0) {
return true;
}
// Patterns de phrases anglaises typiques
const englishPhrases = [
/\b(the|a|an)\s+\w+/i,
/\b(cannot|can't|won't|don't|doesn't)\b/i,
/\b(must|should|will|would)\s+\w+/i,
/\b(has|have|had)\s+(been|gained|lost|acquired)/i,
/\b(enter|choose|select)\s+\w+/i,
/\b(rolled?|passed?|failed?)\b/i,
];
for (const pattern of englishPhrases) {
if (pattern.test(str)) {
return true;
}
}
return false;
}
// Fonction pour filtrer les faux positifs évidents
function shouldIgnore(str) {
// Ignorer les chemins et références système
if (str.includes('Compendium.') || str.includes('UUID[')) {
return true;
}
// Ignorer les noms de propriétés système
if (str.match(/^(system|flags|data|items|effects|actor|token)\./)) {
return true;
}
// Ignorer les expressions JavaScript
if (str.match(/^(let|const|var|function|return|if|else|for|while)\b/)) {
return true;
}
return false;
}
// Analyser tous les fichiers
function analyzeScripts() {
const files = fs.readdirSync(FR_DIR).filter(f => f.endsWith('.js'));
const results = [];
console.log(`Analyse de ${files.length} fichiers...\n`);
files.forEach(file => {
const filePath = path.join(FR_DIR, file);
const content = fs.readFileSync(filePath, 'utf8');
const strings = extractStrings(content);
const englishStrings = strings.filter(str => {
if (shouldIgnore(str)) return false;
return isEnglishText(str);
});
if (englishStrings.length > 0) {
results.push({
file,
count: englishStrings.length,
strings: englishStrings
});
}
});
return results;
}
// Main
console.log('Recherche des textes anglais dans les scripts FR...\n');
console.log('='.repeat(60));
const results = analyzeScripts();
console.log('\n' + '='.repeat(60));
console.log(`Fichiers avec du texte anglais : ${results.length}\n`);
// Trier par nombre de chaînes anglaises (décroissant)
results.sort((a, b) => b.count - a.count);
// Afficher les résultats
let totalStrings = 0;
results.forEach(({ file, count, strings }) => {
totalStrings += count;
console.log(`\n${file} (${count} texte(s) anglais) :`);
console.log('-'.repeat(60));
strings.forEach(str => {
// Limiter la longueur affichée
const displayStr = str.length > 80 ? str.substring(0, 77) + '...' : str;
console.log(`${displayStr}`);
});
});
console.log('\n' + '='.repeat(60));
console.log('Résumé :');
console.log('-'.repeat(60));
console.log(`Fichiers avec texte anglais : ${results.length}/${fs.readdirSync(FR_DIR).filter(f => f.endsWith('.js')).length}`);
console.log(`Total de textes anglais détectés : ${totalStrings}`);
console.log('='.repeat(60));
// Sauvegarder dans un fichier JSON
const outputFile = path.join(__dirname, 'english-texts-found.json');
fs.writeFileSync(outputFile, JSON.stringify(results, null, 2), 'utf8');
console.log(`\nRésultats sauvegardés dans : english-texts-found.json`);

View File

@@ -0,0 +1,103 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const WFRP4E_SCRIPTS = '/home/morr/work/foundryvtt/WFRP4e-FoundryVTT/scripts';
const FR_SCRIPTS = '/home/morr/work/foundryvtt/foundryvtt-wh4-lang-fr-fr/scripts';
const REVIEW_FILE = path.join(__dirname, 'scripts-to-review.json');
const REPORT_FILE = path.join(__dirname, 'review-comparison.md');
const scriptsToReview = JSON.parse(fs.readFileSync(REVIEW_FILE, 'utf-8'));
let report = `# Scripts nécessitant révision manuelle\n\n`;
report += `Total de scripts: ${scriptsToReview.length}\n\n`;
report += `Ces scripts contiennent des traductions en français mais ont été modifiés dans WFRP4E.\n`;
report += `Ils nécessitent une révision manuelle pour intégrer les changements tout en préservant les traductions.\n\n`;
report += `---\n\n`;
scriptsToReview.forEach(({file}, index) => {
const wfrp4ePath = path.join(WFRP4E_SCRIPTS, file);
const frPath = path.join(FR_SCRIPTS, file);
const wfrp4eContent = fs.readFileSync(wfrp4ePath, 'utf-8');
const frContent = fs.readFileSync(frPath, 'utf-8');
report += `## ${index + 1}. ${file}\n\n`;
// Extraire les différences notables
const wfrp4eLines = wfrp4eContent.split('\n').length;
const frLines = frContent.split('\n').length;
report += `- **Lignes WFRP4E**: ${wfrp4eLines}\n`;
report += `- **Lignes FR**: ${frLines}\n`;
report += `- **Différence**: ${wfrp4eLines - frLines} lignes\n\n`;
// Extraire les textes notables
const wfrp4eTexts = extractNotableTexts(wfrp4eContent);
const frTexts = extractNotableTexts(frContent);
if (wfrp4eTexts.length > 0 || frTexts.length > 0) {
report += `### Textes identifiés\n\n`;
if (wfrp4eTexts.length > 0) {
report += `**WFRP4E (anglais)**:\n`;
wfrp4eTexts.forEach(text => {
report += `- \`${text}\`\n`;
});
report += `\n`;
}
if (frTexts.length > 0) {
report += `**FR (français)**:\n`;
frTexts.forEach(text => {
report += `- \`${text}\`\n`;
});
report += `\n`;
}
}
// Afficher le code côte à côte si court
if (wfrp4eLines < 30 && frLines < 30) {
report += `### Code WFRP4E\n\n\`\`\`javascript\n${wfrp4eContent}\n\`\`\`\n\n`;
report += `### Code FR\n\n\`\`\`javascript\n${frContent}\n\`\`\`\n\n`;
}
report += `### Actions recommandées\n\n`;
report += `- [ ] Vérifier les changements structurels dans WFRP4E\n`;
report += `- [ ] Intégrer les modifications tout en gardant les traductions FR\n`;
report += `- [ ] Tester le script après modification\n\n`;
report += `---\n\n`;
});
function extractNotableTexts(content) {
const texts = new Set();
// Patterns pour extraire les textes
const patterns = [
/ui\.notifications\.(info|warn|error|notify)\s*\(\s*["'`]([^"'`]+)["'`]/g,
/this\.script\.(scriptNotification|scriptMessage|notification)\s*\(\s*["'`]([^"'`]+)["'`]/g,
/["'`](Loading|Could not find|Chargement|Impossible de trouver|créé|modifié|supprimé)[^"'`]*["'`]/gi
];
patterns.forEach(pattern => {
const matches = content.matchAll(pattern);
for (const match of matches) {
const text = match[2] || match[1];
if (text && text.length > 3 && text.length < 100) {
texts.add(text);
}
}
});
return Array.from(texts);
}
fs.writeFileSync(REPORT_FILE, report, 'utf-8');
console.log(`\nRapport de révision généré: ${REPORT_FILE}`);
console.log(`\nNombre de scripts à réviser: ${scriptsToReview.length}`);
console.log(`\nOuvrez le fichier avec votre éditeur préféré pour réviser les scripts.`);
console.log(`Vous pouvez aussi utiliser un outil de diff comme 'meld' ou 'kdiff3' pour comparer:`);
console.log(`\nExemple:`);
console.log(` meld ${FR_SCRIPTS}/${scriptsToReview[0].file} ${WFRP4E_SCRIPTS}/${scriptsToReview[0].file}`);

View File

@@ -0,0 +1,193 @@
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');
const TRANSLATIONS_FILE = path.join(__dirname, 'auto-translations-applied.json');
// Charger le mapping de traductions
const translations = JSON.parse(fs.readFileSync(TRANSLATIONS_FILE, 'utf8'));
// Fonction pour générer le diff d'un fichier
function generateFileDiff(file) {
const frPath = path.join(FR_DIR, file);
const enPath = path.join(WFRP4E_DIR, file);
if (!fs.existsSync(frPath) || !fs.existsSync(enPath)) {
return null;
}
const frContent = fs.readFileSync(frPath, 'utf8');
const enContent = fs.readFileSync(enPath, 'utf8');
const changes = [];
// Trouver les traductions appliquées dans ce fichier
for (const [en, fr] of Object.entries(translations)) {
if (frContent.includes(fr) && enContent.includes(en)) {
changes.push({ en, fr });
}
}
return {
file,
changes,
frLines: frContent.split('\n').length,
enLines: enContent.split('\n').length
};
}
// Générer le rapport
console.log('Génération du rapport de traduction...\n');
const modifiedFiles = [];
// Lire tous les fichiers JS du répertoire FR
const files = fs.readdirSync(FR_DIR).filter(f => f.endsWith('.js'));
files.forEach(file => {
const diff = generateFileDiff(file);
if (diff && diff.changes.length > 0) {
modifiedFiles.push(diff);
}
});
// Trier par nombre de changements (décroissant)
modifiedFiles.sort((a, b) => b.changes.length - a.changes.length);
// Générer le rapport Markdown
let report = `# Rapport de Traduction Automatique\n\n`;
report += `**Date**: ${new Date().toLocaleDateString('fr-FR')}\n\n`;
report += `**Fichiers modifiés**: ${modifiedFiles.length}\n`;
report += `**Traductions uniques appliquées**: ${Object.keys(translations).length}\n\n`;
report += `---\n\n`;
// Statistiques globales
const totalChanges = modifiedFiles.reduce((sum, file) => sum + file.changes.length, 0);
report += `## Statistiques\n\n`;
report += `- **Total de fichiers analysés**: ${files.length}\n`;
report += `- **Fichiers modifiés**: ${modifiedFiles.length}\n`;
report += `- **Total de changements**: ${totalChanges}\n`;
report += `- **Moyenne de changements par fichier**: ${(totalChanges / modifiedFiles.length).toFixed(2)}\n\n`;
// Top 20 des fichiers les plus modifiés
report += `## Top 20 des fichiers les plus modifiés\n\n`;
report += `| Fichier | Changements |\n`;
report += `|---------|-------------|\n`;
modifiedFiles.slice(0, 20).forEach(({ file, changes }) => {
report += `| ${file} | ${changes.length} |\n`;
});
report += `\n`;
// Traductions les plus fréquentes
const translationFrequency = new Map();
modifiedFiles.forEach(({ changes }) => {
changes.forEach(({ en, fr }) => {
const key = `${en}${fr}`;
translationFrequency.set(key, (translationFrequency.get(key) || 0) + 1);
});
});
const sortedTranslations = Array.from(translationFrequency.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 30);
report += `## Top 30 des traductions les plus appliquées\n\n`;
report += `| Traduction | Occurrences |\n`;
report += `|------------|-------------|\n`;
sortedTranslations.forEach(([translation, count]) => {
const [en, fr] = translation.split(' → ');
const enShort = en.length > 60 ? en.substring(0, 57) + '...' : en;
const frShort = fr.length > 60 ? fr.substring(0, 57) + '...' : fr;
report += `| \`${enShort}\`\`${frShort}\` | ${count} |\n`;
});
report += `\n`;
// Détails par fichier (exemples significatifs)
report += `## Exemples de modifications détaillées\n\n`;
report += `Voici quelques exemples de fichiers avec leurs traductions appliquées :\n\n`;
modifiedFiles.slice(0, 10).forEach(({ file, changes }) => {
report += `### ${file}\n\n`;
report += `**Nombre de traductions**: ${changes.length}\n\n`;
changes.slice(0, 5).forEach(({ en, fr }) => {
const enDisplay = en.length > 80 ? en.substring(0, 77) + '...' : en;
const frDisplay = fr.length > 80 ? fr.substring(0, 77) + '...' : fr;
report += `- ✏️ \`${enDisplay}\`\n`;
report += `\`${frDisplay}\`\n\n`;
});
if (changes.length > 5) {
report += `... et ${changes.length - 5} autre(s) traduction(s)\n\n`;
}
});
// Traductions potentiellement problématiques
report += `## ⚠️ Traductions à vérifier\n\n`;
report += `Certaines traductions automatiques peuvent nécessiter une révision manuelle :\n\n`;
const problematicPatterns = [
{ pattern: /effet\./, reason: "Vérifier que 'effect' -> 'effet' est approprié dans le contexte" },
{ pattern: /Retiré/, reason: "Vérifier l'accord (retiré/retirée/retirés/retirées)" },
{ pattern: /Ajouté/, reason: "Vérifier l'accord (ajouté/ajoutée/ajoutés/ajoutées)" },
{ pattern: /Appliqué/, reason: "Vérifier l'accord (appliqué/appliquée/appliqués/appliquées)" },
];
const filesToReview = new Map();
modifiedFiles.forEach(({ file, changes }) => {
changes.forEach(({ fr }) => {
problematicPatterns.forEach(({ pattern, reason }) => {
if (pattern.test(fr)) {
if (!filesToReview.has(reason)) {
filesToReview.set(reason, []);
}
filesToReview.get(reason).push({ file, translation: fr });
}
});
});
});
for (const [reason, items] of filesToReview.entries()) {
report += `### ${reason}\n\n`;
const uniqueItems = [...new Map(items.map(item => [item.file, item])).values()].slice(0, 10);
uniqueItems.forEach(({ file, translation }) => {
const transShort = translation.length > 80 ? translation.substring(0, 77) + '...' : translation;
report += `- **${file}**: \`${transShort}\`\n`;
});
if (items.length > 10) {
report += `\n... et ${items.length - 10} autre(s) occurrence(s)\n`;
}
report += `\n`;
}
// Recommandations
report += `## 📋 Recommandations\n\n`;
report += `1. **Vérifier les accords grammaticaux** : Les participes passés (ajouté, retiré, appliqué) doivent s'accorder avec leur sujet.\n`;
report += `2. **Vérifier les contextes** : Certains mots comme "effect" peuvent être du code (à ne pas traduire) ou du texte (à traduire).\n`;
report += `3. **Tester en jeu** : Vérifier que les messages affichés aux joueurs sont grammaticalement corrects.\n`;
report += `4. **Révision manuelle** : Consulter les fichiers avec le plus de changements pour s'assurer de la cohérence.\n\n`;
// Sauvegarder le rapport
const reportFile = path.join(__dirname, 'rapport-traduction-automatique.md');
fs.writeFileSync(reportFile, report, 'utf8');
console.log('='.repeat(60));
console.log('Rapport généré avec succès !');
console.log('='.repeat(60));
console.log(`Fichier : rapport-traduction-automatique.md`);
console.log(`\nStatistiques :`);
console.log(`- ${modifiedFiles.length} fichiers modifiés`);
console.log(`- ${totalChanges} changements au total`);
console.log(`- ${Object.keys(translations).length} traductions uniques`);
console.log('='.repeat(60));
// Afficher un aperçu du rapport
console.log('\n📄 APERÇU DU RAPPORT\n');
console.log(report.split('\n').slice(0, 50).join('\n'));
console.log('\n... (voir le fichier complet pour plus de détails)');

View File

@@ -0,0 +1,154 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const WFRP4E_SCRIPTS = '/home/morr/work/foundryvtt/WFRP4e-FoundryVTT/scripts';
const FR_SCRIPTS = '/home/morr/work/foundryvtt/foundryvtt-wh4-lang-fr-fr/scripts';
const REVIEW_FILE = path.join(__dirname, 'scripts-to-review.json');
const OUTPUT_FILE = path.join(__dirname, 'scripts-replaced-list.md');
const scriptsToReview = JSON.parse(fs.readFileSync(REVIEW_FILE, 'utf-8'));
console.log(`\n=== REMPLACEMENT DES SCRIPTS PAR LES VERSIONS SYSTÈME ===\n`);
console.log(`Nombre de scripts à remplacer: ${scriptsToReview.length}\n`);
let replaced = 0;
let errors = [];
const replacedList = [];
scriptsToReview.forEach(({file, reason}, index) => {
const wfrp4ePath = path.join(WFRP4E_SCRIPTS, file);
const frPath = path.join(FR_SCRIPTS, file);
const backupPath = frPath + '.fr-backup';
try {
// Sauvegarder la version FR avec traductions
if (fs.existsSync(frPath)) {
fs.copyFileSync(frPath, backupPath);
}
// Remplacer par la version WFRP4E
if (fs.existsSync(wfrp4ePath)) {
fs.copyFileSync(wfrp4ePath, frPath);
replaced++;
// Extraire les textes traduits de la sauvegarde pour référence
const frContent = fs.readFileSync(backupPath, 'utf-8');
const translations = extractTranslations(frContent);
replacedList.push({
index: index + 1,
file,
translations
});
console.log(`✓ [${index + 1}/${scriptsToReview.length}] ${file}`);
} else {
errors.push(`Source non trouvée: ${file}`);
console.log(`✗ [${index + 1}/${scriptsToReview.length}] ${file} - Source non trouvée`);
}
} catch (error) {
errors.push(`${file}: ${error.message}`);
console.log(`✗ [${index + 1}/${scriptsToReview.length}] ${file} - Erreur: ${error.message}`);
}
});
function extractTranslations(content) {
const translations = [];
// Patterns pour extraire les textes en français
const patterns = [
/ui\.notifications\.(info|warn|error|notify)\s*\(\s*["'`]([^"'`]+)["'`]/g,
/this\.script\.(scriptNotification|scriptMessage|notification)\s*\(\s*["'`]([^"'`]+)["'`]/g,
/["'`](Chargement|Impossible|Êtes-vous|Voulez-vous|créé|modifié|supprimé|Erreur)[^"'`]*["'`]/gi,
/warhammer\.utility\.findAllItems\([^,]+,\s*["'`]([^"'`]+)["'`]/g
];
patterns.forEach(pattern => {
const regex = new RegExp(pattern.source, pattern.flags);
let match;
while ((match = regex.exec(content)) !== null) {
const text = match[2] || match[1];
if (text && text.length > 3 && /[a-zàâäéèêëïîôöùûüÿçœæ]/i.test(text)) {
if (!translations.includes(text)) {
translations.push(text);
}
}
}
});
return translations;
}
// Générer le rapport
let report = `# Scripts remplacés par les versions système\n\n`;
report += `Date: ${new Date().toISOString()}\n\n`;
report += `## Résumé\n\n`;
report += `- **Scripts remplacés**: ${replaced}\n`;
report += `- **Erreurs**: ${errors.length}\n\n`;
report += `Tous les scripts ont été remplacés par leurs versions du système WFRP4E.\n`;
report += `Les versions françaises avec traductions sont sauvegardées avec l'extension \`.fr-backup\`.\n\n`;
if (errors.length > 0) {
report += `## Erreurs\n\n`;
errors.forEach(error => {
report += `- ${error}\n`;
});
report += `\n`;
}
report += `## Scripts à revoir manuellement\n\n`;
report += `Pour chaque script, vous devez :\n`;
report += `1. Comparer la version actuelle (système) avec le backup (.fr-backup)\n`;
report += `2. Identifier les textes à traduire\n`;
report += `3. Appliquer les traductions nécessaires\n\n`;
report += `| # | Fichier | Traductions trouvées |\n`;
report += `|---|---------|----------------------|\n`;
replacedList.forEach(({index, file, translations}) => {
const translationsText = translations.length > 0
? translations.slice(0, 3).join(', ') + (translations.length > 3 ? '...' : '')
: 'Aucune';
report += `| ${index} | \`${file}\` | ${translationsText} |\n`;
});
report += `\n## Commandes utiles\n\n`;
report += `### Comparer un script avec son backup\n\n`;
report += `\`\`\`bash\n`;
report += `# Avec meld\n`;
report += `meld scripts/<fichier> scripts/<fichier>.fr-backup\n\n`;
report += `# Avec diff\n`;
report += `diff -u scripts/<fichier>.fr-backup scripts/<fichier>\n`;
report += `\`\`\`\n\n`;
report += `### Exemples de scripts\n\n`;
if (replacedList.length > 0) {
const example = replacedList[0];
report += `Premier script: \`${example.file}\`\n\n`;
report += `\`\`\`bash\n`;
report += `meld scripts/${example.file} scripts/${example.file}.fr-backup\n`;
report += `\`\`\`\n\n`;
if (example.translations.length > 0) {
report += `Traductions à réappliquer:\n`;
example.translations.forEach(t => {
report += `- "${t}"\n`;
});
}
}
fs.writeFileSync(OUTPUT_FILE, report, 'utf-8');
console.log(`\n=== RÉSUMÉ ===`);
console.log(`Scripts remplacés: ${replaced}/${scriptsToReview.length}`);
console.log(`Erreurs: ${errors.length}`);
console.log(`\nBackups créés avec extension: .fr-backup`);
console.log(`Liste détaillée: ${OUTPUT_FILE}`);
console.log(`\n=== PROCHAINES ÉTAPES ===`);
console.log(`1. Consulter: ${OUTPUT_FILE}`);
console.log(`2. Pour chaque script, comparer avec son .fr-backup`);
console.log(`3. Réappliquer les traductions nécessaires`);
console.log(`4. Supprimer les .fr-backup une fois terminé\n`);

1489
tools/review-comparison.md Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,713 @@
# Rapport de comparaison des scripts
Date: 2026-01-06T18:58:40.637Z
## Résumé
- **Total de fichiers communs**: 1505
- **Scripts identiques**: 671
- **Scripts nécessitant mise à jour**: 473
- **Scripts utilisant localize() (obsolètes)**: 361
- **Scripts uniquement dans FR**: 2
- **Scripts uniquement dans WFRP4E**: 394
## Scripts obsolètes (utilisant localize() dans WFRP4E)
Ces scripts peuvent être supprimés du module FR car le système utilise maintenant les fichiers de localisation.
- `0FNOq4J1AdPd2A0q.js` - Le système utilise maintenant localize() - script FR obsolète
- `0YKQGbsKdHSmYGE7.js` - Le système utilise maintenant localize() - script FR obsolète
- `0mrUnxzufYgsR0Ph.js` - Le système utilise maintenant localize() - script FR obsolète
- `1A87vGLh2PXH0rG0.js` - Le système utilise maintenant localize() - script FR obsolète
- `1LDSzXeO5CzXgTOc.js` - Le système utilise maintenant localize() - script FR obsolète
- `1kB2su7hLRYDhZ2H.js` - Le système utilise maintenant localize() - script FR obsolète
- `1mNkLj9JYNr3ofC6.js` - Le système utilise maintenant localize() - script FR obsolète
- `1wKVvxRTHOyV4Qdv.js` - Le système utilise maintenant localize() - script FR obsolète
- `22bW97lkvCqisfHX.js` - Le système utilise maintenant localize() - script FR obsolète
- `23HgjCB1oecxANvA.js` - Le système utilise maintenant localize() - script FR obsolète
- `2AdSBXw7IwCiqawQ.js` - Le système utilise maintenant localize() - script FR obsolète
- `2NLINicPQWbuvp2n.js` - Le système utilise maintenant localize() - script FR obsolète
- `2VNnVrtktdGUqXEV.js` - Le système utilise maintenant localize() - script FR obsolète
- `2W9uMTT6iJhfQ044.js` - Le système utilise maintenant localize() - script FR obsolète
- `2WSN306tL4apjRtD.js` - Le système utilise maintenant localize() - script FR obsolète
- `2hzDv8ROulOe1elK.js` - Le système utilise maintenant localize() - script FR obsolète
- `2sDH6RvoOAR40oqH.js` - Le système utilise maintenant localize() - script FR obsolète
- `2vTVR0quRZQtjNfQ.js` - Le système utilise maintenant localize() - script FR obsolète
- `3JEzEzF1SeYA9lsV.js` - Le système utilise maintenant localize() - script FR obsolète
- `3hfMQkUKYI4rCuBy.js` - Le système utilise maintenant localize() - script FR obsolète
- `3plV9WFqs2prfAdp.js` - Le système utilise maintenant localize() - script FR obsolète
- `3sfD1nedXLzuYoXJ.js` - Le système utilise maintenant localize() - script FR obsolète
- `454x3Q95pLvZm0Kx.js` - Le système utilise maintenant localize() - script FR obsolète
- `4FGKZk2f0xrmIDnp.js` - Le système utilise maintenant localize() - script FR obsolète
- `4ZR7p8G3OzOBWx0L.js` - Le système utilise maintenant localize() - script FR obsolète
- `4fnTKgl0HW9ZrWyJ.js` - Le système utilise maintenant localize() - script FR obsolète
- `4iuTz0uInAfMaoGl.js` - Le système utilise maintenant localize() - script FR obsolète
- `4pQW4WLyhjbZR85k.js` - Le système utilise maintenant localize() - script FR obsolète
- `4rb7LfMq9CTnlrpn.js` - Le système utilise maintenant localize() - script FR obsolète
- `52mwb33mGrQjq89B.js` - Le système utilise maintenant localize() - script FR obsolète
- `5Fe1ELaS6Gnvy0Cj.js` - Le système utilise maintenant localize() - script FR obsolète
- `5o1XiceC4rutjMms.js` - Le système utilise maintenant localize() - script FR obsolète
- `5sI9iYh5j2nx2XyT.js` - Le système utilise maintenant localize() - script FR obsolète
- `6JrUjs3g5x6bFnj3.js` - Le système utilise maintenant localize() - script FR obsolète
- `6V3qHON3mcerlBlB.js` - Le système utilise maintenant localize() - script FR obsolète
- `6ZS1rQLkNvMDO0Fp.js` - Le système utilise maintenant localize() - script FR obsolète
- `6fElmBxTjdAaubbK.js` - Le système utilise maintenant localize() - script FR obsolète
- `6qUKKep5vhFYmo1J.js` - Le système utilise maintenant localize() - script FR obsolète
- `6tjn0RH4VyOPFneS.js` - Le système utilise maintenant localize() - script FR obsolète
- `715G1Bf0haOHvmYQ.js` - Le système utilise maintenant localize() - script FR obsolète
- `7Ck0fkzE4WQ62qVe.js` - Le système utilise maintenant localize() - script FR obsolète
- `7H6wYyJ6cpaoc2QQ.js` - Le système utilise maintenant localize() - script FR obsolète
- `7VAhXHov6pR1SkgD.js` - Le système utilise maintenant localize() - script FR obsolète
- `7e8FgQUF2oANANmx.js` - Le système utilise maintenant localize() - script FR obsolète
- `7szLG4VALuuy1cPm.js` - Le système utilise maintenant localize() - script FR obsolète
- `84IB8CWa55XzoAkv.js` - Le système utilise maintenant localize() - script FR obsolète
- `86ivOsBQAuu0UmFg.js` - Le système utilise maintenant localize() - script FR obsolète
- `8ByuHnUZ4RNTdGVv.js` - Le système utilise maintenant localize() - script FR obsolète
- `8N3Uqjq1ZxPxo4pk.js` - Le système utilise maintenant localize() - script FR obsolète
- `8RNziYGGb4sp3BGQ.js` - Le système utilise maintenant localize() - script FR obsolète
- `8ZAUBSH9CM9OTpTL.js` - Le système utilise maintenant localize() - script FR obsolète
- `93K85NnVwjVNXlZq.js` - Le système utilise maintenant localize() - script FR obsolète
- `9EVj4bmZJex45Mt7.js` - Le système utilise maintenant localize() - script FR obsolète
- `9JnPK1jNUEso7Pv8.js` - Le système utilise maintenant localize() - script FR obsolète
- `9RFoasDcFnYZ1txR.js` - Le système utilise maintenant localize() - script FR obsolète
- `9fK07tqqZyPg7dpx.js` - Le système utilise maintenant localize() - script FR obsolète
- `A3fvV69RS1lYgma0.js` - Le système utilise maintenant localize() - script FR obsolète
- `ACgKpKrEEHoNGG0h.js` - Le système utilise maintenant localize() - script FR obsolète
- `AHZ8f7dfN8jNsYk6.js` - Le système utilise maintenant localize() - script FR obsolète
- `ALuPRzf85dmkEfLo.js` - Le système utilise maintenant localize() - script FR obsolète
- `AMxezwtYnWCF6Oza.js` - Le système utilise maintenant localize() - script FR obsolète
- `AV2Kj6jgmIc45zKi.js` - Le système utilise maintenant localize() - script FR obsolète
- `B6ZbY3bxTPg6nCng.js` - Le système utilise maintenant localize() - script FR obsolète
- `B950b0XnIUYCdVwu.js` - Le système utilise maintenant localize() - script FR obsolète
- `BAOv7moTxsKlT3JS.js` - Le système utilise maintenant localize() - script FR obsolète
- `BNJRV66jlrp51qZK.js` - Le système utilise maintenant localize() - script FR obsolète
- `BtyFhdGMKiMamGhM.js` - Le système utilise maintenant localize() - script FR obsolète
- `C4LZnaX0MYgwrszj.js` - Le système utilise maintenant localize() - script FR obsolète
- `CCK1iIfPmB398ziT.js` - Le système utilise maintenant localize() - script FR obsolète
- `CiFeduksZJ6PRulx.js` - Le système utilise maintenant localize() - script FR obsolète
- `CkE8NZOhzPkuRrKJ.js` - Le système utilise maintenant localize() - script FR obsolète
- `DTiHS6RfwhF4THbf.js` - Le système utilise maintenant localize() - script FR obsolète
- `DsE6rTSzxEn6uWMz.js` - Le système utilise maintenant localize() - script FR obsolète
- `DuM5l2Yb4bdvDeaG.js` - Le système utilise maintenant localize() - script FR obsolète
- `DxQnamsb2AuW0p2e.js` - Le système utilise maintenant localize() - script FR obsolète
- `E4CHDe1xfmcV3oGv.js` - Le système utilise maintenant localize() - script FR obsolète
- `E6DMqfDeczqmVMFV.js` - Le système utilise maintenant localize() - script FR obsolète
- `E7D4bxz8gy4e1wL7.js` - Le système utilise maintenant localize() - script FR obsolète
- `EDAMEOzdBfkoKHxP.js` - Le système utilise maintenant localize() - script FR obsolète
- `EGWF3LHav3e2zFL4.js` - Le système utilise maintenant localize() - script FR obsolète
- `EQ5dtGW5kQhtAb87.js` - Le système utilise maintenant localize() - script FR obsolète
- `EU5j0hnDTG9Z6d1e.js` - Le système utilise maintenant localize() - script FR obsolète
- `EVBSHEC5nmmj2X41.js` - Le système utilise maintenant localize() - script FR obsolète
- `EmXwcuycEH8slEn5.js` - Le système utilise maintenant localize() - script FR obsolète
- `EmmG49pMOPHRwDzR.js` - Le système utilise maintenant localize() - script FR obsolète
- `Ew3C2WmLCtc1KT46.js` - Le système utilise maintenant localize() - script FR obsolète
- `FXuyiJoXdAh6WhRK.js` - Le système utilise maintenant localize() - script FR obsolète
- `FYUPfYyTYZkxRLFT.js` - Le système utilise maintenant localize() - script FR obsolète
- `FkTwk8hfHpRLbAp2.js` - Le système utilise maintenant localize() - script FR obsolète
- `FmLx9pwOkzqqU7Ph.js` - Le système utilise maintenant localize() - script FR obsolète
- `Fvlc4RkeF4dHjW3m.js` - Le système utilise maintenant localize() - script FR obsolète
- `GOq4TcnWbfyfCo2V.js` - Le système utilise maintenant localize() - script FR obsolète
- `GZFsuynUhgZqwTGo.js` - Le système utilise maintenant localize() - script FR obsolète
- `GbPEy22VuCNzlNv2.js` - Le système utilise maintenant localize() - script FR obsolète
- `Gc8S5TYlVdV8NnOT.js` - Le système utilise maintenant localize() - script FR obsolète
- `H2CJvApKMnfGNNoo.js` - Le système utilise maintenant localize() - script FR obsolète
- `HASsi6wYHVALExWq.js` - Le système utilise maintenant localize() - script FR obsolète
- `HKhyn0kijKfzW6cw.js` - Le système utilise maintenant localize() - script FR obsolète
- `HMC6hPND9DWLFfZE.js` - Le système utilise maintenant localize() - script FR obsolète
- `HUKzssLxeQo1wbx9.js` - Le système utilise maintenant localize() - script FR obsolète
- `Hcpn1gU58DIKIhty.js` - Le système utilise maintenant localize() - script FR obsolète
- `HdTuY2IGhdlMDOy3.js` - Le système utilise maintenant localize() - script FR obsolète
- `HfCxNd7mFGZH4s9Y.js` - Le système utilise maintenant localize() - script FR obsolète
- `HfZAQWoY8n3clLlo.js` - Le système utilise maintenant localize() - script FR obsolète
- `HiMBS6GeOwEydWYN.js` - Le système utilise maintenant localize() - script FR obsolète
- `HrOBAXsEX073ReKl.js` - Le système utilise maintenant localize() - script FR obsolète
- `I7ieW0hNYvvX0KFg.js` - Le système utilise maintenant localize() - script FR obsolète
- `I9QAPKbaXwMMMBT4.js` - Le système utilise maintenant localize() - script FR obsolète
- `ID8mCcjkl7PCQhDq.js` - Le système utilise maintenant localize() - script FR obsolète
- `IPPDvZdE8kn3H9z7.js` - Le système utilise maintenant localize() - script FR obsolète
- `IR5URcjnCuWBFMoN.js` - Le système utilise maintenant localize() - script FR obsolète
- `IsLAvY9ikR1cOJWw.js` - Le système utilise maintenant localize() - script FR obsolète
- `J0IWUhxada2ONowP.js` - Le système utilise maintenant localize() - script FR obsolète
- `JBoKPBr27C3PMoSD.js` - Le système utilise maintenant localize() - script FR obsolète
- `JEbs0WlqhKNDOo5A.js` - Le système utilise maintenant localize() - script FR obsolète
- `JQruHprM5R5vZ9DA.js` - Le système utilise maintenant localize() - script FR obsolète
- `JeThJbOf6Xmbtgo1.js` - Le système utilise maintenant localize() - script FR obsolète
- `JgCcgDVZX54slrWx.js` - Le système utilise maintenant localize() - script FR obsolète
- `JhCMYHHYA860Kka8.js` - Le système utilise maintenant localize() - script FR obsolète
- `JibNjuQrJRnY0yf9.js` - Le système utilise maintenant localize() - script FR obsolète
- `Jk7OHqx06oCUVAzb.js` - Le système utilise maintenant localize() - script FR obsolète
- `JstrA46EYSEuRSy5.js` - Le système utilise maintenant localize() - script FR obsolète
- `JyTxUG5dNW670Sf7.js` - Le système utilise maintenant localize() - script FR obsolète
- `KIoVBinAZK8sMOqD.js` - Le système utilise maintenant localize() - script FR obsolète
- `KPQfupKuaf4LCv4R.js` - Le système utilise maintenant localize() - script FR obsolète
- `KSjsDlsx3DD6cT16.js` - Le système utilise maintenant localize() - script FR obsolète
- `KTBVDHUndI3qDOXM.js` - Le système utilise maintenant localize() - script FR obsolète
- `KUx0deSF3xNzMucL.js` - Le système utilise maintenant localize() - script FR obsolète
- `KkjkYAGI9Em1NgiQ.js` - Le système utilise maintenant localize() - script FR obsolète
- `KyUPYV1RXJxPOfyA.js` - Le système utilise maintenant localize() - script FR obsolète
- `L9eAtDyaoHvqryk4.js` - Le système utilise maintenant localize() - script FR obsolète
- `LeKLtvEDrWh4yHsx.js` - Le système utilise maintenant localize() - script FR obsolète
- `Lg1oRg4oNRvucsvi.js` - Le système utilise maintenant localize() - script FR obsolète
- `LjfScVGBf0Egr70E.js` - Le système utilise maintenant localize() - script FR obsolète
- `Lpv2N9LK9loeumiW.js` - Le système utilise maintenant localize() - script FR obsolète
- `Lrb1S2aK7SFVD0C7.js` - Le système utilise maintenant localize() - script FR obsolète
- `Lu27iXtJVkrJ8bOx.js` - Le système utilise maintenant localize() - script FR obsolète
- `M0XhKnWjG14pk3iH.js` - Le système utilise maintenant localize() - script FR obsolète
- `M2FshTX4PjKFVU8y.js` - Le système utilise maintenant localize() - script FR obsolète
- `M9VgeYGiUO97ZUW4.js` - Le système utilise maintenant localize() - script FR obsolète
- `MJB6WbZSF6Briz30.js` - Le système utilise maintenant localize() - script FR obsolète
- `MMv2B8TH7jxNCtdl.js` - Le système utilise maintenant localize() - script FR obsolète
- `MMx8f5uJ15XOgJGg.js` - Le système utilise maintenant localize() - script FR obsolète
- `MSJWJEUrX7ZmMvAD.js` - Le système utilise maintenant localize() - script FR obsolète
- `Mp8UjuCLa9jdzhBS.js` - Le système utilise maintenant localize() - script FR obsolète
- `N9kA46ZQ9LiRjy6T.js` - Le système utilise maintenant localize() - script FR obsolète
- `NDvNGATDM2xPFJK0.js` - Le système utilise maintenant localize() - script FR obsolète
- `NkqtQaLEGYst0PUO.js` - Le système utilise maintenant localize() - script FR obsolète
- `NnA4zpuO6iUXKhH7.js` - Le système utilise maintenant localize() - script FR obsolète
- `NnDq4miIPNEtZUWb.js` - Le système utilise maintenant localize() - script FR obsolète
- `Np8P8IPT2GYlrh6G.js` - Le système utilise maintenant localize() - script FR obsolète
- `OeCgX3hIsfLfmGvC.js` - Le système utilise maintenant localize() - script FR obsolète
- `OgSI0Z6SHjYuqKIz.js` - Le système utilise maintenant localize() - script FR obsolète
- `OqLKL0PiCvYH2QxW.js` - Le système utilise maintenant localize() - script FR obsolète
- `P29tT1eMMdAw3CPF.js` - Le système utilise maintenant localize() - script FR obsolète
- `PMISUsaEDBA7D3od.js` - Le système utilise maintenant localize() - script FR obsolète
- `PMNjdEAusVBfam09.js` - Le système utilise maintenant localize() - script FR obsolète
- `PVjaKAHTKDA0rA9J.js` - Le système utilise maintenant localize() - script FR obsolète
- `PdClojv7yNgQpOUc.js` - Le système utilise maintenant localize() - script FR obsolète
- `PoNnT5EqvLj2r5yf.js` - Le système utilise maintenant localize() - script FR obsolète
- `Q4EQgP4gZR8TTm7S.js` - Le système utilise maintenant localize() - script FR obsolète
- `Q5gh5Y9dCdclcwsD.js` - Le système utilise maintenant localize() - script FR obsolète
- `Q9EmlWmuDac83cJw.js` - Le système utilise maintenant localize() - script FR obsolète
- `QPVVDPcJ4Xi5FmQl.js` - Le système utilise maintenant localize() - script FR obsolète
- `QQ2gHThZHdO4yLLX.js` - Le système utilise maintenant localize() - script FR obsolète
- `QfTBRGXVfwQSghmd.js` - Le système utilise maintenant localize() - script FR obsolète
- `RDtJXji3hgcKnEBk.js` - Le système utilise maintenant localize() - script FR obsolète
- `RHyBLYT5oHf7EPnG.js` - Le système utilise maintenant localize() - script FR obsolète
- `RNr9CwyvLhlnwD2h.js` - Le système utilise maintenant localize() - script FR obsolète
- `RprZWlnopSqZt7KZ.js` - Le système utilise maintenant localize() - script FR obsolète
- `RrchOMpEdIvceJxl.js` - Le système utilise maintenant localize() - script FR obsolète
- `SNjG7IvgQzvCGczR.js` - Le système utilise maintenant localize() - script FR obsolète
- `SrCHfOJFZwDickqa.js` - Le système utilise maintenant localize() - script FR obsolète
- `T3RfSt3VMEat3iDD.js` - Le système utilise maintenant localize() - script FR obsolète
- `TOyQLrugFGzwc5nY.js` - Le système utilise maintenant localize() - script FR obsolète
- `TU2xjbJ0zFtytF3J.js` - Le système utilise maintenant localize() - script FR obsolète
- `TdjlJro0RRVSh8g8.js` - Le système utilise maintenant localize() - script FR obsolète
- `ToKlHDAjJOsC51ag.js` - Le système utilise maintenant localize() - script FR obsolète
- `Tq45Nd8J3eTvHT41.js` - Le système utilise maintenant localize() - script FR obsolète
- `TrIYdcG3jDER4WgY.js` - Le système utilise maintenant localize() - script FR obsolète
- `TwwKxFjK6TNGKbRB.js` - Le système utilise maintenant localize() - script FR obsolète
- `U4A8FDNmwBGTuQZO.js` - Le système utilise maintenant localize() - script FR obsolète
- `U78c4oUjKwyUu6jT.js` - Le système utilise maintenant localize() - script FR obsolète
- `U7lRf56BFaTfsC6B.js` - Le système utilise maintenant localize() - script FR obsolète
- `U9xh2wriSqCosugW.js` - Le système utilise maintenant localize() - script FR obsolète
- `UC6G3pFQwzUhrw6F.js` - Le système utilise maintenant localize() - script FR obsolète
- `UTlgF4azrGQZsoDv.js` - Le système utilise maintenant localize() - script FR obsolète
- `UaImulhbPURkLJg2.js` - Le système utilise maintenant localize() - script FR obsolète
- `UsAHQecRlWL5PhP6.js` - Le système utilise maintenant localize() - script FR obsolète
- `UvycHOjSPm5Zv9KJ.js` - Le système utilise maintenant localize() - script FR obsolète
- `VAs1nXvicdQK18Do.js` - Le système utilise maintenant localize() - script FR obsolète
- `VCnNWmRBH28Rx0JH.js` - Le système utilise maintenant localize() - script FR obsolète
- `VNOKTzCrFbZ6PJUj.js` - Le système utilise maintenant localize() - script FR obsolète
- `VQaC6ynouW5EQCbC.js` - Le système utilise maintenant localize() - script FR obsolète
- `VbL0TgaBAmYjrezZ.js` - Le système utilise maintenant localize() - script FR obsolète
- `VlFUDaKUYoBYKYn0.js` - Le système utilise maintenant localize() - script FR obsolète
- `VlHuDIAvPr8JM1P5.js` - Le système utilise maintenant localize() - script FR obsolète
- `Vns6WrafVq8NjX7t.js` - Le système utilise maintenant localize() - script FR obsolète
- `VqTrKPtxv9hpn0Hr.js` - Le système utilise maintenant localize() - script FR obsolète
- `VurA2XNqC0d9U49C.js` - Le système utilise maintenant localize() - script FR obsolète
- `VzO0ZDcTWvaBn4vm.js` - Le système utilise maintenant localize() - script FR obsolète
- `WTRYAEzwxRuGENyI.js` - Le système utilise maintenant localize() - script FR obsolète
- `Wqff6ICWYzvLDYC8.js` - Le système utilise maintenant localize() - script FR obsolète
- `XClTZOjuhcxWLF51.js` - Le système utilise maintenant localize() - script FR obsolète
- `Xf4FcZ4ZLdWmOXBa.js` - Le système utilise maintenant localize() - script FR obsolète
- `XuFxKPYheYF4WMmQ.js` - Le système utilise maintenant localize() - script FR obsolète
- `YDqpKJVy8ijEKdLX.js` - Le système utilise maintenant localize() - script FR obsolète
- `YEBw2CzHYxsTpTy5.js` - Le système utilise maintenant localize() - script FR obsolète
- `YFSgWRXel0bWE3EE.js` - Le système utilise maintenant localize() - script FR obsolète
- `YMaxpXnml01bLGpO.js` - Le système utilise maintenant localize() - script FR obsolète
- `YhhiWEQbox3WZA3Q.js` - Le système utilise maintenant localize() - script FR obsolète
- `Z6OmmC0irJzxSnMH.js` - Le système utilise maintenant localize() - script FR obsolète
- `ZLspFT2I3ZdWWr2H.js` - Le système utilise maintenant localize() - script FR obsolète
- `ZNW3ubbKIYXAyE48.js` - Le système utilise maintenant localize() - script FR obsolète
- `Ze9E2zR1GFGWGMVH.js` - Le système utilise maintenant localize() - script FR obsolète
- `ZgF1sYdbAKaOuHmK.js` - Le système utilise maintenant localize() - script FR obsolète
- `ZvbBM4gTJHPdU8jU.js` - Le système utilise maintenant localize() - script FR obsolète
- `a0YU3whUm16wGBNu.js` - Le système utilise maintenant localize() - script FR obsolète
- `a4Aza4a9v8JMU8dC.js` - Le système utilise maintenant localize() - script FR obsolète
- `a6gacHsvgwtsIFSQ.js` - Le système utilise maintenant localize() - script FR obsolète
- `aAvJrAKLzXhS9qN6.js` - Le système utilise maintenant localize() - script FR obsolète
- `afdmOvPGMpEdZvCb.js` - Le système utilise maintenant localize() - script FR obsolète
- `b5prg1FLjCAvBjLy.js` - Le système utilise maintenant localize() - script FR obsolète
- `bh2qDCKiAIWM0UPi.js` - Le système utilise maintenant localize() - script FR obsolète
- `bhXUW7IJtEaUGAoV.js` - Le système utilise maintenant localize() - script FR obsolète
- `bwx9wuEOJPF7btD1.js` - Le système utilise maintenant localize() - script FR obsolète
- `c3KA1knL2NYNUEww.js` - Le système utilise maintenant localize() - script FR obsolète
- `cAKqUBuJBsU85erO.js` - Le système utilise maintenant localize() - script FR obsolète
- `cTYNaKbepMwqYsZ8.js` - Le système utilise maintenant localize() - script FR obsolète
- `cV9JiAgUPOeUHKnS.js` - Le système utilise maintenant localize() - script FR obsolète
- `cqYgZQk1C3kMARy5.js` - Le système utilise maintenant localize() - script FR obsolète
- `dAMtbn3uQwF8KpKU.js` - Le système utilise maintenant localize() - script FR obsolète
- `dSmRhZ6o1bUcOSF9.js` - Le système utilise maintenant localize() - script FR obsolète
- `dXGosSxdoYbET2O8.js` - Le système utilise maintenant localize() - script FR obsolète
- `dfEgat3jz4EtWszH.js` - Le système utilise maintenant localize() - script FR obsolète
- `dfnCK8jCPXNLM7Gh.js` - Le système utilise maintenant localize() - script FR obsolète
- `dkiNmwFXztIVbuxg.js` - Le système utilise maintenant localize() - script FR obsolète
- `dsXGavzXLerXR8Xg.js` - Le système utilise maintenant localize() - script FR obsolète
- `dtFvpY96RPzNnphq.js` - Le système utilise maintenant localize() - script FR obsolète
- `dwSgcFxKN3S0hLJL.js` - Le système utilise maintenant localize() - script FR obsolète
- `eGQAyzCFZAEQhyJl.js` - Le système utilise maintenant localize() - script FR obsolète
- `f062aa2BNClx08D6.js` - Le système utilise maintenant localize() - script FR obsolète
- `f3rXusHh6VIpVPUl.js` - Le système utilise maintenant localize() - script FR obsolète
- `f5Mp3kXwCFdPkW6N.js` - Le système utilise maintenant localize() - script FR obsolète
- `fKzlu6fFE2th9uIJ.js` - Le système utilise maintenant localize() - script FR obsolète
- `fP2edYbj6QjPPAjL.js` - Le système utilise maintenant localize() - script FR obsolète
- `fYLSe6CrYciap5y8.js` - Le système utilise maintenant localize() - script FR obsolète
- `g07HI7vyqiFXPB0o.js` - Le système utilise maintenant localize() - script FR obsolète
- `gFUXBbTskQBKjxqm.js` - Le système utilise maintenant localize() - script FR obsolète
- `gKPL3t4vlZAsvtGr.js` - Le système utilise maintenant localize() - script FR obsolète
- `gnVpxOeBZpNF4HIF.js` - Le système utilise maintenant localize() - script FR obsolète
- `gqZLfIr6svrtdwdC.js` - Le système utilise maintenant localize() - script FR obsolète
- `gu72JaTs9GrSiVTd.js` - Le système utilise maintenant localize() - script FR obsolète
- `h0DfPwUUOBjyAHMZ.js` - Le système utilise maintenant localize() - script FR obsolète
- `h1XKoMuVnS0bagRO.js` - Le système utilise maintenant localize() - script FR obsolète
- `hDC6lroDEPVBituR.js` - Le système utilise maintenant localize() - script FR obsolète
- `hR1qD2kpFHF8JT8h.js` - Le système utilise maintenant localize() - script FR obsolète
- `hnsmzvcuiUYB065I.js` - Le système utilise maintenant localize() - script FR obsolète
- `iM6JLF8jDXMViReZ.js` - Le système utilise maintenant localize() - script FR obsolète
- `iT0h3VZLEBQnn5Bx.js` - Le système utilise maintenant localize() - script FR obsolète
- `iT3C15fMyQrj1RmG.js` - Le système utilise maintenant localize() - script FR obsolète
- `jRSgPhpfN7MH7TTp.js` - Le système utilise maintenant localize() - script FR obsolète
- `jgO1Kf60Ctt6R0qO.js` - Le système utilise maintenant localize() - script FR obsolète
- `jpcU8FFWSlQ3gD0L.js` - Le système utilise maintenant localize() - script FR obsolète
- `k3FqFgsF6a3TkxAD.js` - Le système utilise maintenant localize() - script FR obsolète
- `k8TC0yzp4xfOXD2n.js` - Le système utilise maintenant localize() - script FR obsolète
- `kEaBLoIHt1FpQVKq.js` - Le système utilise maintenant localize() - script FR obsolète
- `kMSdRskYDI2J1gnp.js` - Le système utilise maintenant localize() - script FR obsolète
- `kWynO1lQzjiSs8RK.js` - Le système utilise maintenant localize() - script FR obsolète
- `kYiDBPRKIokFkr4Z.js` - Le système utilise maintenant localize() - script FR obsolète
- `kYmscP2HuXjDovBD.js` - Le système utilise maintenant localize() - script FR obsolète
- `kkC5EhqA05U6U0gU.js` - Le système utilise maintenant localize() - script FR obsolète
- `kmsGLWGxCY8Z8jVG.js` - Le système utilise maintenant localize() - script FR obsolète
- `l1ntrpa8RE3Lg5xE.js` - Le système utilise maintenant localize() - script FR obsolète
- `l8qFKSnMpy4P7XQR.js` - Le système utilise maintenant localize() - script FR obsolète
- `lPudo1grrVp05i7a.js` - Le système utilise maintenant localize() - script FR obsolète
- `lYYkGzqNshiYc7WI.js` - Le système utilise maintenant localize() - script FR obsolète
- `lhemR8EP5tGNKout.js` - Le système utilise maintenant localize() - script FR obsolète
- `mTnmPcjWmvScIBWY.js` - Le système utilise maintenant localize() - script FR obsolète
- `mYL4i1vNlMl4vFYy.js` - Le système utilise maintenant localize() - script FR obsolète
- `mr8qm5Bg6k1idZ6Q.js` - Le système utilise maintenant localize() - script FR obsolète
- `mziJBUYcsrhkdcCJ.js` - Le système utilise maintenant localize() - script FR obsolète
- `nD2QVg3DrPK3foMf.js` - Le système utilise maintenant localize() - script FR obsolète
- `nEaF3jbCiVYD8jia.js` - Le système utilise maintenant localize() - script FR obsolète
- `nIOeK0BrtdZeZW8X.js` - Le système utilise maintenant localize() - script FR obsolète
- `ncIjDE6TFx88IQA1.js` - Le système utilise maintenant localize() - script FR obsolète
- `nvrFhHHVq3KzirlR.js` - Le système utilise maintenant localize() - script FR obsolète
- `o3JUBKLvE6bBxK2n.js` - Le système utilise maintenant localize() - script FR obsolète
- `oEsUsI74yGTZ9CwU.js` - Le système utilise maintenant localize() - script FR obsolète
- `oGdsGPgJWcyWkiWl.js` - Le système utilise maintenant localize() - script FR obsolète
- `oWM43EdUiyHgUlfW.js` - Le système utilise maintenant localize() - script FR obsolète
- `obIXhQXKFyyQoNNV.js` - Le système utilise maintenant localize() - script FR obsolète
- `p222EiR8RRtlPm31.js` - Le système utilise maintenant localize() - script FR obsolète
- `p7NJB1PsY6X5q8OO.js` - Le système utilise maintenant localize() - script FR obsolète
- `pAgiAGyaLJY10894.js` - Le système utilise maintenant localize() - script FR obsolète
- `pHyXdPnWwoFrUA5n.js` - Le système utilise maintenant localize() - script FR obsolète
- `pNPjXEoQGHLKzq0r.js` - Le système utilise maintenant localize() - script FR obsolète
- `pq1YQffxtOcqCRTn.js` - Le système utilise maintenant localize() - script FR obsolète
- `pzknBcJtZWeliE27.js` - Le système utilise maintenant localize() - script FR obsolète
- `qTk96NKh14YOIShf.js` - Le système utilise maintenant localize() - script FR obsolète
- `qaVde0sTuMBRi2nl.js` - Le système utilise maintenant localize() - script FR obsolète
- `qijgjcOBCDmhglRX.js` - Le système utilise maintenant localize() - script FR obsolète
- `quPcuKsq2fcild4a.js` - Le système utilise maintenant localize() - script FR obsolète
- `qzGPJflVW7c2Ciim.js` - Le système utilise maintenant localize() - script FR obsolète
- `rXMylpnEtZpwou6x.js` - Le système utilise maintenant localize() - script FR obsolète
- `red2bt4PGgIWAdTR.js` - Le système utilise maintenant localize() - script FR obsolète
- `rlTvhnDpj4lzfusf.js` - Le système utilise maintenant localize() - script FR obsolète
- `rnioLwiJP7ps5Jwy.js` - Le système utilise maintenant localize() - script FR obsolète
- `roKvPHDSpX4IV11C.js` - Le système utilise maintenant localize() - script FR obsolète
- `rr4htuVq45onXdRG.js` - Le système utilise maintenant localize() - script FR obsolète
- `rxB1AqfVMDqTRieE.js` - Le système utilise maintenant localize() - script FR obsolète
- `s7gJQdzuM3fz2zQK.js` - Le système utilise maintenant localize() - script FR obsolète
- `sSMg43ZjnNmpytfj.js` - Le système utilise maintenant localize() - script FR obsolète
- `sUHuj00IpoZ7Gd03.js` - Le système utilise maintenant localize() - script FR obsolète
- `sWyO1DNaqb2EdHoW.js` - Le système utilise maintenant localize() - script FR obsolète
- `sagKJM6oDFlw4ED2.js` - Le système utilise maintenant localize() - script FR obsolète
- `sgS9rblPkQB36C8S.js` - Le système utilise maintenant localize() - script FR obsolète
- `syBgPp7rOymCZejX.js` - Le système utilise maintenant localize() - script FR obsolète
- `t56mnblo1kv3gM1M.js` - Le système utilise maintenant localize() - script FR obsolète
- `tDrs0aNIYmZPsOoS.js` - Le système utilise maintenant localize() - script FR obsolète
- `tG1qGqzBLmAR3WHm.js` - Le système utilise maintenant localize() - script FR obsolète
- `tcZug8ehU6lyYBd9.js` - Le système utilise maintenant localize() - script FR obsolète
- `tn6SwmjAuNzqE6dx.js` - Le système utilise maintenant localize() - script FR obsolète
- `tnE8LF6E3svIlLca.js` - Le système utilise maintenant localize() - script FR obsolète
- `tnilBagajWM8UGQt.js` - Le système utilise maintenant localize() - script FR obsolète
- `tolkNN5P7oOC8GKZ.js` - Le système utilise maintenant localize() - script FR obsolète
- `tuIsytv3jl2yAIb2.js` - Le système utilise maintenant localize() - script FR obsolète
- `uACa6u4JLNMRgFlr.js` - Le système utilise maintenant localize() - script FR obsolète
- `uSwkYENTOcnfRrqA.js` - Le système utilise maintenant localize() - script FR obsolète
- `upfK6GM33qZCZjll.js` - Le système utilise maintenant localize() - script FR obsolète
- `uwdQvZtzy2HVeEzx.js` - Le système utilise maintenant localize() - script FR obsolète
- `ux3jaLpAUhZ5YqA6.js` - Le système utilise maintenant localize() - script FR obsolète
- `v6LIG7EwWYALxCpP.js` - Le système utilise maintenant localize() - script FR obsolète
- `vj3zArmX27vCxYJW.js` - Le système utilise maintenant localize() - script FR obsolète
- `vvdo1NxjmGVkmFCT.js` - Le système utilise maintenant localize() - script FR obsolète
- `vzMxIDjRlQSxXtCW.js` - Le système utilise maintenant localize() - script FR obsolète
- `w4JKuTECFmu3YmTR.js` - Le système utilise maintenant localize() - script FR obsolète
- `wS6OIR3QN4mOUHn0.js` - Le système utilise maintenant localize() - script FR obsolète
- `wsqNM4NHnwaAAQym.js` - Le système utilise maintenant localize() - script FR obsolète
- `wzkxiKjxVEeNS1di.js` - Le système utilise maintenant localize() - script FR obsolète
- `x2RLUUNB7BiIDYCP.js` - Le système utilise maintenant localize() - script FR obsolète
- `x8XK2fzzXCnSwfjt.js` - Le système utilise maintenant localize() - script FR obsolète
- `xRd6oA1QWLVpfoBm.js` - Le système utilise maintenant localize() - script FR obsolète
- `xS2su09zcza9du09.js` - Le système utilise maintenant localize() - script FR obsolète
- `xVYVegD692CCv8Yl.js` - Le système utilise maintenant localize() - script FR obsolète
- `xWPgIZOQLba5nZ23.js` - Le système utilise maintenant localize() - script FR obsolète
- `xobGBJbgFjQCrwvq.js` - Le système utilise maintenant localize() - script FR obsolète
- `xvH4PKJasHcehOZz.js` - Le système utilise maintenant localize() - script FR obsolète
- `yIGItnshXI4W46cp.js` - Le système utilise maintenant localize() - script FR obsolète
- `yRRzSKlcN3L7rEEe.js` - Le système utilise maintenant localize() - script FR obsolète
- `yXUPiWuQ5o0JBY4i.js` - Le système utilise maintenant localize() - script FR obsolète
- `yatUu0cd7iKs50rq.js` - Le système utilise maintenant localize() - script FR obsolète
- `z5HfNUrHy5MS3K7a.js` - Le système utilise maintenant localize() - script FR obsolète
- `zA6TDttpwMSMl7D8.js` - Le système utilise maintenant localize() - script FR obsolète
- `zFYCaGR0wbFWjl4T.js` - Le système utilise maintenant localize() - script FR obsolète
- `zLBLEDl3aGcAxai2.js` - Le système utilise maintenant localize() - script FR obsolète
- `zR4WAjzXHTZuiu5G.js` - Le système utilise maintenant localize() - script FR obsolète
- `zY4ZjY7uyKIKMYmX.js` - Le système utilise maintenant localize() - script FR obsolète
- `ziG9H5Kdb7xC3dFE.js` - Le système utilise maintenant localize() - script FR obsolète
- `zsiV6XEHlCNGLLPl.js` - Le système utilise maintenant localize() - script FR obsolète
- `zzk2uRSFjFHB5kjw.js` - Le système utilise maintenant localize() - script FR obsolète
## Scripts nécessitant une mise à jour
### 03mJu59V4iByWlsO.js
- Taille WFRP4E: 1153 octets
- Taille FR: 1201 octets
- Textes FR: <p><strong>${this.actor.prototypeToken.name}</strong> :
<ul>
<li>Reçoit 3 états Empoisonnés, sans Test de Résistance possible</li>
<li>Récupère ${this.actor.system.characteristics.t.bonus} Blessures</li>
<li>Acuiert le Trait de Creature Régénération.</li>
</ul>
C
### 0BP5l7bIkf744G1k.js
- Taille WFRP4E: 70 octets
- Taille FR: 70 octets
### 0FWto1oEr3jbWggw.js
- Taille WFRP4E: 789 octets
- Taille FR: 766 octets
- Textes WFRP4E: Could not find ${lore} spell. Try Again
- Textes FR: Impossible de trouver le sort ${lore}. Essayez à nouveau
### 0R0QAr3D024kWPfo.js
- Taille WFRP4E: 552 octets
- Taille FR: 589 octets
- Textes WFRP4E: Mutation table not found, please ensure a table with the , Item could not be found: , ${item.name} added
- Textes FR: La table des Mutations n, L, ${item.name} added
### 0Uly7OmkH0zqYbxQ.js
- Taille WFRP4E: 584 octets
- Taille FR: 585 octets
- Textes WFRP4E: Dropped ${dropped.map(i => i.name).join(
- Textes FR: Lache ${dropped.map(i => i.name).join(
### 0abwNjpzo3SbEOeO.js
- Taille WFRP4E: 267 octets
- Taille FR: 270 octets
### 0yyofYHeDRQlFliO.js
- Taille WFRP4E: 68 octets
- Taille FR: 68 octets
### 11uCC0mK2uL783al.js
- Taille WFRP4E: 134 octets
- Taille FR: 133 octets
### 190PHSHKGaJ74wsR.js
- Taille WFRP4E: 1169 octets
- Taille FR: 1060 octets
### 1BT0MWM2cbhlEnrn.js
- Taille WFRP4E: 1169 octets
- Taille FR: 1176 octets
- Textes WFRP4E: Could not find ${talent}
- Textes FR: Impossible de trouver ${talent}
### 1CeYp5MlPcH68UIw.js
- Taille WFRP4E: 2481 octets
- Taille FR: 2498 octets
- Textes WFRP4E: Could not find ${talent}, Could not find ${trapping}
- Textes FR: Impossible de trouver ${talent}, Impossible de trouver ${trapping}
### 1JwxZujbDcueLWBL.js
- Taille WFRP4E: 162 octets
- Taille FR: 168 octets
### 1OpT3CXs07XFWWCT.js
- Taille WFRP4E: 30 octets
- Taille FR: 30 octets
### 1ZArMNUI8qqH6zkX.js
- Taille WFRP4E: 247 octets
- Taille FR: 242 octets
### 1exiWlVUHsXDLLAH.js
- Taille WFRP4E: 62 octets
- Taille FR: 70 octets
- Textes WFRP4E: Cannot enter ${this.effect.name}!
- Textes FR: Ne peut saisir ${this.effect.name}!
### 1fQr1Dg7DX0vfz3r.js
- Taille WFRP4E: 134 octets
- Taille FR: 145 octets
- Textes FR: Soins de ${healed} Blessures
### 1jQq9v8fXK8zuEBU.js
- Taille WFRP4E: 853 octets
- Taille FR: 863 octets
### 1l7Jz2ZHbAWko7Vm.js
- Taille WFRP4E: 33 octets
- Taille FR: 33 octets
### 1wrPvP6lJwIAfmsl.js
- Taille WFRP4E: 362 octets
- Taille FR: 371 octets
### 1yOvw74jzFfaI87b.js
- Taille WFRP4E: 27 octets
- Taille FR: 27 octets
### 23vWiHUjxtRQ3Efz.js
- Taille WFRP4E: 219 octets
- Taille FR: 218 octets
### 2AOo7KUyzMrgIlgM.js
- Taille WFRP4E: 137 octets
- Taille FR: 129 octets
### 2Qi1hwLYhdE6v6Hs.js
- Taille WFRP4E: 243 octets
- Taille FR: 258 octets
- Textes WFRP4E: Cannot Dodge!
- Textes FR: Impossible d
### 2cKarG9ToyW2ptCd.js
- Taille WFRP4E: 338 octets
- Taille FR: 340 octets
- Textes WFRP4E: None left!
- Textes FR: Vide !
### 2rzxQlL7A9ujZ9uK.js
- Taille WFRP4E: 315 octets
- Taille FR: 316 octets
- Textes WFRP4E: <b>${this.effect.name}</b>: Gained ${this.item.Advances} Advantage
- Textes FR: <b>${this.effect.name}</b>: Gain de ${this.item.Advances} Avantages
### 2yctEihGmdCfTTVx.js
- Taille WFRP4E: 88 octets
- Taille FR: 63 octets
### 2zeP2nMSURjxrqYz.js
- Taille WFRP4E: 282 octets
- Taille FR: 302 octets
- Textes WFRP4E: No effect at 0 Wounds, Healed ${this.actor.characteristics.t.bonus} Wounds
- Textes FR: Aucun effet à 0 Blessures, Soin de ${this.actor.characteristics.t.bonus} Blessures
### 33US8YRgaMqYu2We.js
- Taille WFRP4E: 29 octets
- Taille FR: 29 octets
### 37UN17gb8suFeZIW.js
- Taille WFRP4E: 133 octets
- Taille FR: 139 octets
### 3D5ImpMgpOTPucvv.js
- Taille WFRP4E: 379 octets
- Taille FR: 379 octets
### 3IGO7xEjRjat937X.js
- Taille WFRP4E: 232 octets
- Taille FR: 241 octets
- Textes WFRP4E: Removing Fatigued Condition, disabled effect
- Textes FR: Suppression de l
### 3eSaX0BeaUalNkEP.js
- Taille WFRP4E: 26 octets
- Taille FR: 26 octets
### 3jm0NoYpgB6ZuUSl.js
- Taille WFRP4E: 38 octets
- Taille FR: 38 octets
### 3l7MQSa10Kve2K3P.js
- Taille WFRP4E: 215 octets
- Taille FR: 221 octets
### 4MQ7u4INxp51oyyR.js
- Taille WFRP4E: 1932 octets
- Taille FR: 1952 octets
- Textes WFRP4E: Could not find ${talent}, Could not find ${trapping}
- Textes FR: Impossible de trouver ${talent}, Impossible de trouver ${trapping}
### 4ylzjgUdHY5D0yVh.js
- Taille WFRP4E: 3410 octets
- Taille FR: 3359 octets
- Textes WFRP4E: Could not find ${talent}, Could not find ${trapping}
- Textes FR: Impossible de trouver ${talent}, Impossible de trouver ${trapping}
### 58rFc9HiBoX66J6p.js
- Taille WFRP4E: 444 octets
- Taille FR: 453 octets
### 5DI6cHAg1LHo54Yv.js
- Taille WFRP4E: 420 octets
- Taille FR: 426 octets
- Textes FR: 1 Point de Fortune volé à <strong>${this.actor.name}</strong>
### 5JWC0l3JEpOsqbR9.js
- Taille WFRP4E: 1767 octets
- Taille FR: 1783 octets
- Textes WFRP4E: Could not find ${talent}, Could not find ${trapping}
- Textes FR: Impossible de trouver ${talent}, Impossible de trouver ${trapping}
### 5JvKJZPcd6Hz5zvn.js
- Taille WFRP4E: 1886 octets
- Taille FR: 1906 octets
- Textes WFRP4E: Could not find ${talent}, Could not find ${trapping}
- Textes FR: Impossible de trouver ${talent}, Impossible de trouver ${trapping}
### 5M6IgCUncCwyxHok.js
- Taille WFRP4E: 1217 octets
- Taille FR: 1197 octets
### 5MxRDXzUBPfp2KKD.js
- Taille WFRP4E: 38 octets
- Taille FR: 38 octets
### 5ScjIdRUOxblHdWS.js
- Taille WFRP4E: 1217 octets
- Taille FR: 1212 octets
### 5dR7Erj3nwsxLAV7.js
- Taille WFRP4E: 1954 octets
- Taille FR: 1976 octets
- Textes WFRP4E: Could not find ${talent}, Could not find ${trapping}
- Textes FR: Impossible de trouver ${talent}, Impossible de trouver ${trapping}
### 62Ky6bC1EnTllSJA.js
- Taille WFRP4E: 253 octets
- Taille FR: 259 octets
### 65xE9OV5sA1ZWT7j.js
- Taille WFRP4E: 377 octets
- Taille FR: 377 octets
### 6Aqq4F4Xui923sc6.js
- Taille WFRP4E: 779 octets
- Taille FR: 754 octets
- Textes FR: <p><strong>${this.actor.prototypeToken.name}</strong> reçoit le Trait de Créatuer A Sang Froid et peut inverser n
### 6BmvV9c03FkfisnE.js
- Taille WFRP4E: 1069 octets
- Taille FR: 1107 octets
- Textes FR: ${this.actor.name} porte le <strong>${this.item.name}</strong>. <br>
Il gagne +1 Point de Corruption point si un Test de Corruption est échoué, à appliquer manuellement.<br>Si le personnage porte le masque pendant plus d, <strong>${this.item.name}</strong> de ${this.actor.name} a été enlevé et a perdu ses propriétés. Cependant, les effets perdurent pendant [[1d10+4]] jours, après quoi ils devront être manuellement supprimés.
### 6H6vNjzvMGl2ZqCR.js
- Taille WFRP4E: 285 octets
- Taille FR: 305 octets
- Textes FR: <b>Lame de Nurglitch: ${args.actor.name}</b> doit réussir un <b>Test d
### 6QZUX7ZrFxOzqI0b.js
- Taille WFRP4E: 252 octets
- Taille FR: 268 octets
... et 423 autres fichiers
## Scripts uniquement dans WFRP4E (394)
Ces scripts existent dans WFRP4E mais pas dans le module FR.
- 07tvKnPT8ICtv2us.js
- 0H2syk6qc0sCY0pj.js
- 0IAc5VFR2ogXOaGE.js
- 0NAD1LNcVyAZ1fC7.js
- 0PzfGjGhHQbxEwlb.js
- 0amHqfjTRp5ff6Op.js
- 0hAbiNR1nFkpFMRz.js
- 0hgTyeaEhMBLwzUn.js
- 0kUalAsb4OhtYbaJ.js
- 0uUxvBLJC76WIIBC.js
- 0wR0LWpfhLFA240I.js
- 0wmIC2MssUX6LW3N.js
- 1Du4e27M8WgP2iui.js
- 1PPh4vHd5sPKtudZ.js
- 1UvL0XA1EpQtjDNn.js
- 20fvE9nJmcfGYVzw.js
- 22PMOW0G5MWiBQmg.js
- 2EAYX5G9Fh3HJwiP.js
- 2Ej6LUqn8frTRURo.js
- 2IPTF0akWw9y48br.js
... et 374 autres
## Scripts uniquement dans FR (2)
Ces scripts existent dans le module FR mais plus dans WFRP4E (probablement supprimés).
- ZingXNvYyZ2BEvfY.js
- tfdKU6nmvUtEjXE2.js

View File

@@ -0,0 +1,93 @@
# Scripts remplacés par les versions système
Date: 2026-01-06T19:07:20.939Z
## Résumé
- **Scripts remplacés**: 46
- **Erreurs**: 0
Tous les scripts ont été remplacés par leurs versions du système WFRP4E.
Les versions françaises avec traductions sont sauvegardées avec l'extension `.fr-backup`.
## Scripts à revoir manuellement
Pour chaque script, vous devez :
1. Comparer la version actuelle (système) avec le backup (.fr-backup)
2. Identifier les textes à traduire
3. Appliquer les traductions nécessaires
| # | Fichier | Traductions trouvées |
|---|---------|----------------------|
| 1 | `0FWto1oEr3jbWggw.js` | Impossible de trouver le sort ${lore}. Essayez à nouveau, Chargement, Impossible... |
| 2 | `1BT0MWM2cbhlEnrn.js` | Impossible de trouver ${talent}, Impossible |
| 3 | `1CeYp5MlPcH68UIw.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 4 | `2Qi1hwLYhdE6v6Hs.js` | Impossible d, Impossible |
| 5 | `4MQ7u4INxp51oyyR.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 6 | `4ylzjgUdHY5D0yVh.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 7 | `5JWC0l3JEpOsqbR9.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 8 | `5JvKJZPcd6Hz5zvn.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 9 | `5dR7Erj3nwsxLAV7.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 10 | `6jcTzK1XEuWqAacN.js` | Impossible d, Impossible |
| 11 | `6xTtJEhRc4OjcDTf.js` | Impossible de mettre à jour la Fortune, Impossible |
| 12 | `A1odAcuRbq9797ZB.js` | Impossible de trouver ${talent}, Impossible |
| 13 | `I1J2m5uud84N50Lk.js` | Impossible d, Impossible |
| 14 | `IslMfFgpgQq2brpu.js` | Impossible de supprimer l, Impossible |
| 15 | `IupskvzvoGyD2H5o.js` | Adding , Impossible de trouver any Blessings associated with ${god}., Impossible... |
| 16 | `KICZPwLvbUSxbDrE.js` | Impossible de trouver la table des Mutations Mentales, Impossible de trouver l, ${item.name} added... |
| 17 | `N8hA6ysHCTlLd8Kj.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 18 | `RvipIYj9H7n4UDMe.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 19 | `TCtXPvDpbfz1yrVZ.js` | Impossible de trouver ${talent}, Impossible |
| 20 | `TwgdEucxcHloc4cX.js` | Impossible de trouver ${c.name}, Impossible |
| 21 | `U1UpSRJOSjPpO4HS.js` | Ajout ${mutations[roll].name}, Chargement, Chargement des mutations |
| 22 | `ULmZMLezDamerN04.js` | Chargement, Chargement des sorts |
| 23 | `UQtXuQmUlTyDKqhe.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 24 | `UnqnWi0dZYLf5fTT.js` | Impossible de trouver ${c.name}, Impossible |
| 25 | `V9zm2hKUVLVZtAcN.js` | Impossible de trouver ${talent}, Impossible de trouver ${trait}, Impossible de trouver ${trapping}... |
| 26 | `WO5n7Vbx8AgoVrwC.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 27 | `YN8719gme9AxYtyY.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 28 | `Z6jNlyZMECkW4Hon.js` | Chargement, Chargement des sorts |
| 29 | `aeBetniKqq5SD9Ou.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 30 | `bEVlJOOA1kLlzpWx.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 31 | `ePPgxQOqL1Uhz2k9.js` | Impossible de trouver ${talent}, Impossible |
| 32 | `h766UvswLCsxcMow.js` | Impossible de trouver ${talent}, Impossible de trouver ${trait}, Impossible de trouver ${trapping}... |
| 33 | `hhv7PrRdlf9sfC82.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 34 | `hpwJRAhCsXTp9bd9.js` | Impossible de trouver ${talent}, Impossible |
| 35 | `iuSoKntfJ4eAPafQ.js` | Chargement, Chargement des sorts |
| 36 | `jHgxpe6TJDlP3oTn.js` | Chargement, Chargement des sorts |
| 37 | `lII4KMRblqwFBlsV.js` | Impossible de trouver ${talent}, Impossible de trouver ${trait}, Impossible de trouver ${trapping}... |
| 38 | `oTXPA6rbPnYOKNmo.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 39 | `okW06V9UiPC4Vcrn.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 40 | `svCqdytEOtqFXCcs.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 41 | `sz0PqS1kroMOzUZk.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 42 | `v5xrDWcrTNFJkyQB.js` | Impossible de trouver ${talent}, Impossible |
| 43 | `whUSkaR1yem21bXp.js` | Impossible de trouver ${talent}, Impossible de trouver ${trait}, Impossible de trouver ${trapping}... |
| 44 | `y3F1K1sYBymPZCjz.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 45 | `z8mwLihZQu0JtUHY.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
| 46 | `zadppy7FEbXvgUON.js` | Impossible de trouver ${talent}, Impossible de trouver ${trapping}, Impossible |
## Commandes utiles
### Comparer un script avec son backup
```bash
# Avec meld
meld scripts/<fichier> scripts/<fichier>.fr-backup
# Avec diff
diff -u scripts/<fichier>.fr-backup scripts/<fichier>
```
### Exemples de scripts
Premier script: `0FWto1oEr3jbWggw.js`
```bash
meld scripts/0FWto1oEr3jbWggw.js scripts/0FWto1oEr3jbWggw.js.fr-backup
```
Traductions à réappliquer:
- "Impossible de trouver le sort ${lore}. Essayez à nouveau"
- "Chargement"
- "Impossible"
- "Chargement des sorts"

View File

@@ -0,0 +1,186 @@
[
{
"file": "0FWto1oEr3jbWggw.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "1BT0MWM2cbhlEnrn.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "1CeYp5MlPcH68UIw.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "2Qi1hwLYhdE6v6Hs.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "4MQ7u4INxp51oyyR.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "4ylzjgUdHY5D0yVh.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "5JWC0l3JEpOsqbR9.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "5JvKJZPcd6Hz5zvn.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "5dR7Erj3nwsxLAV7.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "6jcTzK1XEuWqAacN.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "6xTtJEhRc4OjcDTf.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "A1odAcuRbq9797ZB.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "I1J2m5uud84N50Lk.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "IslMfFgpgQq2brpu.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "IupskvzvoGyD2H5o.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "KICZPwLvbUSxbDrE.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "N8hA6ysHCTlLd8Kj.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "RvipIYj9H7n4UDMe.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "TCtXPvDpbfz1yrVZ.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "TwgdEucxcHloc4cX.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "U1UpSRJOSjPpO4HS.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "ULmZMLezDamerN04.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "UQtXuQmUlTyDKqhe.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "UnqnWi0dZYLf5fTT.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "V9zm2hKUVLVZtAcN.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "WO5n7Vbx8AgoVrwC.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "YN8719gme9AxYtyY.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "Z6jNlyZMECkW4Hon.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "aeBetniKqq5SD9Ou.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "bEVlJOOA1kLlzpWx.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "ePPgxQOqL1Uhz2k9.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "h766UvswLCsxcMow.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "hhv7PrRdlf9sfC82.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "hpwJRAhCsXTp9bd9.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "iuSoKntfJ4eAPafQ.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "jHgxpe6TJDlP3oTn.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "lII4KMRblqwFBlsV.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "oTXPA6rbPnYOKNmo.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "okW06V9UiPC4Vcrn.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "svCqdytEOtqFXCcs.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "sz0PqS1kroMOzUZk.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "v5xrDWcrTNFJkyQB.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "whUSkaR1yem21bXp.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "y3F1K1sYBymPZCjz.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "z8mwLihZQu0JtUHY.js",
"reason": "Contient des textes traduits en français"
},
{
"file": "zadppy7FEbXvgUON.js",
"reason": "Contient des textes traduits en français"
}
]

196
tools/sync-scripts.js Normal file
View File

@@ -0,0 +1,196 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const WFRP4E_SCRIPTS = '/home/morr/work/foundryvtt/WFRP4e-FoundryVTT/scripts';
const FR_SCRIPTS = '/home/morr/work/foundryvtt/foundryvtt-wh4-lang-fr-fr/scripts';
const REPORT_FILE = '/home/morr/work/foundryvtt/foundryvtt-wh4-lang-fr-fr/tools/script-comparison-report.md';
// Recherche des patterns qui indiquent du texte localisé
const LOCALIZE_PATTERNS = [
/game\.i18n\.localize\(/g,
/game\.i18n\.format\(/g,
/game\.wfrp4e\.i18n\./g
];
// Patterns de textes en dur qui ont probablement été traduits
const HARDCODED_TEXT_PATTERNS = [
/ui\.notifications\.(info|warn|error|notify)\s*\(\s*["'`]([^"'`]+)["'`]/g,
/this\.script\.(scriptNotification|scriptMessage|notification)\s*\(\s*["'`]([^"'`]+)["'`]/g
];
function analyzeScript(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
// Vérifie si le fichier utilise localize()
const hasLocalize = LOCALIZE_PATTERNS.some(pattern => pattern.test(content));
// Extrait les textes en dur
const hardcodedTexts = [];
HARDCODED_TEXT_PATTERNS.forEach(pattern => {
const matches = content.matchAll(pattern);
for (const match of matches) {
hardcodedTexts.push(match[2] || match[1]);
}
});
return {
hasLocalize,
hardcodedTexts,
content,
size: content.length
};
}
function compareScripts() {
const wfrp4eFiles = fs.readdirSync(WFRP4E_SCRIPTS).filter(f => f.endsWith('.js'));
const frFiles = fs.readdirSync(FR_SCRIPTS).filter(f => f.endsWith('.js'));
const results = {
identical: [],
needsUpdate: [],
usesLocalize: [],
onlyInFR: [],
onlyInWFRP4E: [],
total: 0
};
// Fichiers présents seulement dans le module FR
frFiles.forEach(file => {
if (!wfrp4eFiles.includes(file)) {
results.onlyInFR.push(file);
}
});
// Fichiers présents seulement dans WFRP4E
wfrp4eFiles.forEach(file => {
if (!frFiles.includes(file)) {
results.onlyInWFRP4E.push(file);
}
});
// Comparaison des fichiers communs
const commonFiles = wfrp4eFiles.filter(f => frFiles.includes(f));
results.total = commonFiles.length;
commonFiles.forEach(file => {
const wfrp4ePath = path.join(WFRP4E_SCRIPTS, file);
const frPath = path.join(FR_SCRIPTS, file);
const wfrp4e = analyzeScript(wfrp4ePath);
const fr = analyzeScript(frPath);
// Si WFRP4E utilise localize(), le script FR n'est plus nécessaire
if (wfrp4e.hasLocalize) {
results.usesLocalize.push({
file,
details: 'Le système utilise maintenant localize() - script FR obsolète'
});
}
// Si les contenus sont identiques
else if (wfrp4e.content === fr.content) {
results.identical.push(file);
}
// Si différents, besoin de mise à jour
else {
results.needsUpdate.push({
file,
wfrp4eSize: wfrp4e.size,
frSize: fr.size,
wfrp4eTexts: wfrp4e.hardcodedTexts,
frTexts: fr.hardcodedTexts
});
}
});
return results;
}
function generateReport(results) {
let report = `# Rapport de comparaison des scripts\n\n`;
report += `Date: ${new Date().toISOString()}\n\n`;
report += `## Résumé\n\n`;
report += `- **Total de fichiers communs**: ${results.total}\n`;
report += `- **Scripts identiques**: ${results.identical.length}\n`;
report += `- **Scripts nécessitant mise à jour**: ${results.needsUpdate.length}\n`;
report += `- **Scripts utilisant localize() (obsolètes)**: ${results.usesLocalize.length}\n`;
report += `- **Scripts uniquement dans FR**: ${results.onlyInFR.length}\n`;
report += `- **Scripts uniquement dans WFRP4E**: ${results.onlyInWFRP4E.length}\n\n`;
if (results.usesLocalize.length > 0) {
report += `## Scripts obsolètes (utilisant localize() dans WFRP4E)\n\n`;
report += `Ces scripts peuvent être supprimés du module FR car le système utilise maintenant les fichiers de localisation.\n\n`;
results.usesLocalize.forEach(({file, details}) => {
report += `- \`${file}\` - ${details}\n`;
});
report += `\n`;
}
if (results.needsUpdate.length > 0) {
report += `## Scripts nécessitant une mise à jour\n\n`;
results.needsUpdate.slice(0, 50).forEach(item => {
report += `### ${item.file}\n\n`;
report += `- Taille WFRP4E: ${item.wfrp4eSize} octets\n`;
report += `- Taille FR: ${item.frSize} octets\n`;
if (item.wfrp4eTexts.length > 0) {
report += `- Textes WFRP4E: ${item.wfrp4eTexts.join(', ')}\n`;
}
if (item.frTexts.length > 0) {
report += `- Textes FR: ${item.frTexts.join(', ')}\n`;
}
report += `\n`;
});
if (results.needsUpdate.length > 50) {
report += `\n... et ${results.needsUpdate.length - 50} autres fichiers\n\n`;
}
}
if (results.onlyInWFRP4E.length > 0) {
report += `## Scripts uniquement dans WFRP4E (${results.onlyInWFRP4E.length})\n\n`;
report += `Ces scripts existent dans WFRP4E mais pas dans le module FR.\n\n`;
results.onlyInWFRP4E.slice(0, 20).forEach(file => {
report += `- ${file}\n`;
});
if (results.onlyInWFRP4E.length > 20) {
report += `\n... et ${results.onlyInWFRP4E.length - 20} autres\n`;
}
report += `\n`;
}
if (results.onlyInFR.length > 0) {
report += `## Scripts uniquement dans FR (${results.onlyInFR.length})\n\n`;
report += `Ces scripts existent dans le module FR mais plus dans WFRP4E (probablement supprimés).\n\n`;
results.onlyInFR.slice(0, 20).forEach(file => {
report += `- ${file}\n`;
});
if (results.onlyInFR.length > 20) {
report += `\n... et ${results.onlyInFR.length - 20} autres\n`;
}
report += `\n`;
}
return report;
}
console.log('Analyse des scripts en cours...');
const results = compareScripts();
const report = generateReport(results);
fs.writeFileSync(REPORT_FILE, report, 'utf-8');
console.log(`\nRapport généré: ${REPORT_FILE}`);
console.log(`\nRésumé:`);
console.log(` Identiques: ${results.identical.length}`);
console.log(` À mettre à jour: ${results.needsUpdate.length}`);
console.log(` Obsolètes (localize): ${results.usesLocalize.length}`);
console.log(` Uniquement FR: ${results.onlyInFR.length}`);
console.log(` Uniquement WFRP4E: ${results.onlyInWFRP4E.length}`);
// Export des résultats pour utilisation ultérieure
fs.writeFileSync(
path.join(path.dirname(REPORT_FILE), 'script-comparison-data.json'),
JSON.stringify(results, null, 2),
'utf-8'
);

81
tools/translate-ids.js Normal file
View File

@@ -0,0 +1,81 @@
import fs from 'fs';
import path from 'path';
// Lire et parser le JSON
const jsonPath = './compendium/wfrp4e-core.items.json';
console.log('Lecture du fichier JSON...');
const jsonData = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
// Créer le mapping id -> name
const idToName = {};
jsonData.entries.forEach(entry => {
if (entry.id && entry.name) {
idToName[entry.id] = entry.name;
}
});
console.log(`Nombre d'entrées trouvées: ${Object.keys(idToName).length}`);
// Fonction pour échapper les regex
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// Parcourir tous les fichiers .js dans scripts/
const scriptsDir = './scripts/';
console.log('Analyse des fichiers dans', scriptsDir);
const files = fs.readdirSync(scriptsDir).filter(f => f.endsWith('.js'));
console.log(`${files.length} fichiers .js trouvés`);
let totalReplacements = 0;
let filesModified = 0;
const modifications = {};
files.forEach(file => {
const filePath = path.join(scriptsDir, file);
let content = fs.readFileSync(filePath, 'utf8');
let originalContent = content;
let fileReplacements = 0;
// Pour chaque ID, chercher et remplacer les occurrences entre guillemets
Object.entries(idToName).forEach(([id, name]) => {
// Chercher l'ID entre guillemets simples ou doubles
const pattern1 = new RegExp('"' + escapeRegExp(id) + '"', 'g');
const pattern2 = new RegExp("'" + escapeRegExp(id) + "'", 'g');
const matches1 = (content.match(pattern1) || []).length;
const matches2 = (content.match(pattern2) || []).length;
if (matches1 > 0 || matches2 > 0) {
content = content.replace(pattern1, '"' + name + '"');
content = content.replace(pattern2, "'" + name + "'");
if (!modifications[file]) modifications[file] = [];
modifications[file].push(` ${id} -> ${name} (${matches1 + matches2} fois)`);
fileReplacements += matches1 + matches2;
}
});
if (content !== originalContent) {
fs.writeFileSync(filePath, content, 'utf8');
filesModified++;
totalReplacements += fileReplacements;
}
});
console.log('\n===== RÉSULTAT =====');
console.log(`Fichiers modifiés: ${filesModified} / ${files.length}`);
console.log(`Total de remplacements: ${totalReplacements}`);
if (filesModified > 0) {
console.log('\n===== DÉTAILS DES MODIFICATIONS =====');
Object.entries(modifications).forEach(([file, changes]) => {
console.log(`\n${file}:`);
changes.slice(0, 10).forEach(change => console.log(change));
if (changes.length > 10) {
console.log(` ... et ${changes.length - 10} autres remplacements`);
}
});
}
console.log('\n✓ Traitement terminé !');

177
tools/translate-scripts.js Normal file
View File

@@ -0,0 +1,177 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Mappings de traduction basés sur fr.json
const translations = {
// Choose phrases
"Choose 12 Arcane Spells": "Choisissez 12 Sorts d'Arcane",
"Choose 1 Necromancy Spell": "Choisissez 1 Sort de Nécromancie",
"Choose 2 Arcane Spells": "Choisissez 2 Sorts d'Arcane",
"Choose 2 Skills to add +20": "Choisissez 2 Compétences pour ajouter +20",
"Choose 3 Arcane Spells": "Choisissez 3 Sorts d'Arcane",
"Choose 3 from the Lore of Necromancy": "Choisissez 3 dans le Savoir de Nécromancie",
"Choose 3 Petty Spells": "Choisissez 3 Sorts Mineurs",
"Choose 4 Petty Spells": "Choisissez 4 Sorts Mineurs",
"Choose 6 Arcane Spells": "Choisissez 6 Sorts d'Arcane",
"Choose 6 Petty Spells": "Choisissez 6 Sorts Mineurs",
"Choose 7 taken from any combination of spells from Colour Magic Lore, the Lore of Witchcraft, or Lore of Dark Magic": "Choisissez 7 Sorts parmi toute combinaison de Magie de Couleur, Sorcellerie ou Magie Noire",
"Choose 8 Arcane Spells & Lore of Death": "Choisissez 8 Sorts d'Arcane et Savoir de la Mort",
"Choose 9 Arcane Spells": "Choisissez 9 Sorts d'Arcane",
"Choose a characteristic": "Choisissez une caractéristique",
"Choose an appropriate Polearm or Two-Handed Weapon": "Choisissez une arme d'hast ou une arme à deux mains appropriée",
"Choose Chanty": "Choisissez un Chant",
"Choose Double Life Career": "Choisissez une Carrière de Double Vie",
"Choose Lore": "Choisissez un Savoir",
"Choose Rune": "Choisissez une Rune",
"Choose Sense": "Choisissez un Sens",
// Enter phrases
"Enter Armour value": "Entrez la valeur d'Armure",
"Enter Etiquette Group": "Entrez le Groupe d'Étiquette",
"Enter Fear value": "Entrez la valeur de Peur",
"Enter Hatred Group": "Entrez le Groupe de Haine",
"Enter Resistance": "Entrez la Résistance",
"Enter Spellcasting Lore": "Entrez le Savoir d'Incantation",
"Enter Target Species": "Entrez l'Espèce Cible",
"Enter Target Species (singular)": "Entrez l'Espèce Cible (singulier)",
"Enter Terror value": "Entrez la valeur de Terreur",
"Enter the Armoured value": "Entrez la valeur d'Armure",
"Enter the number of diseases/poisons cured": "Entrez le nombre de maladies/poisons soignés",
"Enter the Sturdy value": "Entrez la valeur de Robustesse",
"Enter Venom Strength": "Entrez la Force du Venin",
"Enter Ward value": "Entrez la valeur de Protection",
"Enter Wounds Lost to gain SL": "Entrez les Blessures Perdues pour gagner DR",
// Select phrases
"Select Column": "Sélectionnez une Colonne",
"Select Rune": "Sélectionnez une Rune",
"Select Target": "Sélectionnez une Cible",
"Select Target for Hatred": "Sélectionnez une Cible pour la Haine",
"Select the column to roll on to determine Beast Head": "Sélectionnez la colonne de lancer pour déterminer la Tête de Bête",
// Damage/Combat terms
"Add Metal AP to Damage": "Ajouter PA de métal aux Dégâts",
"Blunt Damage Reduction": "Réduction des Dégâts Contondants",
"Critical (if successful attack)": "Critique (si attaque réussie)",
"Damage Increase": "Augmentation des Dégâts",
"Damage Reduction": "Réduction des Dégâts",
"Remove Damage Rating": "Retirer l'Indice de Dégâts",
"Double Wounds + 4": "Blessures Doublées + 4",
"Double Wounds vs Dragons": "Blessures Doublées vs Dragons",
// Status/Values
"Armoured Value": "Valeur d'Armure",
"Halved": "Divisé par deux",
"Hatred Group": "Groupe de Haine",
"Sturdy Value": "Valeur de Robustesse",
// Equipment
"Mail": "Mailles",
"Mail & Leather": "Mailles et Cuir",
"Two-Handed": "A deux mains",
// Actions
"Misfire": "Raté",
"Misfire (Supercharged)": "Raté (Surchargé)",
"Roll": "Lancer",
// Misc
"Basic": "Base",
"Choice": "Choix",
"Provide an tattooist Actor (close to skip Tests)": "Fournissez un Acteur tatoueur (fermer pour ignorer les Tests)",
"Skill": "Compétence",
"Target has Aethyric Attunement or Second Sight": "La Cible possède Harmonisation Aethyrique ou Seconde Vue",
"Victory Notes for Experience Log": "Notes de Victoire pour le Journal d'Expérience"
};
// Fonction pour échapper les caractères spéciaux pour regex
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// Fonction pour traiter un fichier
function translateFile(filePath) {
let content = fs.readFileSync(filePath, 'utf8');
let modified = false;
// Appliquer chaque traduction
for (const [english, french] of Object.entries(translations)) {
const escapedEnglish = escapeRegExp(english);
// Patterns pour détecter les contextes (label, text, title, details, message)
const patterns = [
new RegExp(`(label\\s*:\\s*["'\`])${escapedEnglish}(["'\`])`, 'g'),
new RegExp(`(text\\s*:\\s*["'\`])${escapedEnglish}(["'\`])`, 'g'),
new RegExp(`(title\\s*:\\s*["'\`])${escapedEnglish}(["'\`])`, 'g'),
new RegExp(`(details\\s*:\\s*["'\`])${escapedEnglish}(["'\`])`, 'g'),
new RegExp(`(message\\s*:\\s*["'\`])${escapedEnglish}(["'\`])`, 'g'),
];
patterns.forEach(pattern => {
if (pattern.test(content)) {
content = content.replace(pattern, `$1${french}$2`);
modified = true;
}
});
}
if (modified) {
fs.writeFileSync(filePath, content, 'utf8');
return true;
}
return false;
}
// Fonction pour traiter récursivement un répertoire
function processDirectory(dirPath) {
const files = fs.readdirSync(dirPath);
let stats = {
total: 0,
modified: 0,
errors: 0
};
files.forEach(file => {
const filePath = path.join(dirPath, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
const subStats = processDirectory(filePath);
stats.total += subStats.total;
stats.modified += subStats.modified;
stats.errors += subStats.errors;
} else if (file.endsWith('.js')) {
stats.total++;
try {
if (translateFile(filePath)) {
stats.modified++;
console.log(`${path.relative(process.cwd(), filePath)}`);
}
} catch (error) {
stats.errors++;
console.error(`${path.relative(process.cwd(), filePath)}: ${error.message}`);
}
}
});
return stats;
}
// Main
console.log('Début de la traduction des scripts...\n');
const scriptsDir = path.join(__dirname, 'scripts');
const stats = processDirectory(scriptsDir);
console.log('\n' + '='.repeat(60));
console.log('Traduction terminée !');
console.log('='.repeat(60));
console.log(`Fichiers traités : ${stats.total}`);
console.log(`Fichiers modifiés : ${stats.modified}`);
console.log(`Erreurs : ${stats.errors}`);
console.log('='.repeat(60));

File diff suppressed because it is too large Load Diff