// ======================================== // FICHIER: MicroEnhancements.js // RESPONSABILITÉ: Micro-améliorations subtiles (phrases courtes, ponctuation) // Variations très légères pour plus de naturel // ======================================== const { logSh } = require('../ErrorReporting'); /** * MICRO-PHRASES D'INSERTION (2-3 mots) * Petites incises naturelles qui cassent la monotonie */ const MICRO_INSERTIONS = { // Incises temporelles temporal: [ 'aujourd\'hui', 'actuellement', 'de nos jours', 'désormais', 'dorénavant' ], // Incises de renforcement (début de phrase) reinforcement: [ 'En effet', 'Effectivement', 'Bien sûr', 'Naturellement', 'Évidemment' ], // Incises de nuance nuance: [ 'sans doute', 'bien entendu', 'en général', 'le plus souvent', 'dans l\'ensemble', 'sans aucun doute', // ✅ Nouveau 'il faut dire', // ✅ Nouveau 'à noter' // ✅ Nouveau ], // Transitions courtes transition: [ 'par exemple', 'notamment', 'entre autres', 'en particulier', 'qui plus est', // ✅ Nouveau 'point important', // ✅ Nouveau 'à souligner' // ✅ Nouveau ] }; /** * VARIATIONS DE PONCTUATION * Remplacement point par point-virgule ou deux-points dans certains cas */ const PUNCTUATION_PATTERNS = [ // Point → Point-virgule (pour lier deux phrases courtes apparentées) { pattern: /\. ([A-ZÉÈÊ][a-zéèêàùô]{2,20}) (est|sont|permet|offre|assure|garantit|reste|propose)/, replacement: ' ; $1 $2', probability: 0.25, description: 'Liaison phrases courtes apparentées' }, // Point → Deux-points (avant explication/liste) { pattern: /\. (Ces|Cette|Ce|Votre|Notre|Les) ([a-zéèêàùô\s]{5,40}) (sont|est|offre|offrent|permet|garantit|assure)/, replacement: ' : $1 $2 $3', probability: 0.2, description: 'Introduction explication' } ]; /** * APPLICATION MICRO-INSERTIONS * @param {string} text - Texte à enrichir * @param {object} options - { intensity, maxInsertions } * @returns {object} - { content, insertions } */ function applyMicroInsertions(text, options = {}) { const config = { intensity: 0.3, maxInsertions: 2, ...options }; if (!text || text.trim().length === 0) { return { content: text, insertions: 0 }; } let modified = text; let insertions = 0; try { const sentences = modified.split(/\. (?=[A-Z])/); if (sentences.length < 3) { return { content: text, insertions: 0 }; // Texte trop court } // Insertion en début de phrase (après le premier tiers) if (Math.random() < config.intensity && insertions < config.maxInsertions) { const targetIndex = Math.floor(sentences.length / 3); const reinforcements = MICRO_INSERTIONS.reinforcement; const chosen = reinforcements[Math.floor(Math.random() * reinforcements.length)]; // Vérifier que la phrase ne commence pas déjà par une incise if (!sentences[targetIndex].match(/^(En effet|Effectivement|Bien sûr|Naturellement)/)) { sentences[targetIndex] = chosen + ', ' + sentences[targetIndex].toLowerCase(); insertions++; logSh(` ✨ Micro-insertion: "${chosen}"`, 'DEBUG'); } } // Insertion temporelle (milieu du texte) - ✅ DRASTIQUEMENT RÉDUIT // Probabilité réduite de 0.8 → 0.05 (-94%) car souvent inapproprié if (Math.random() < config.intensity * 0.05 && insertions < config.maxInsertions) { const targetIndex = Math.floor(sentences.length / 2); const temporals = MICRO_INSERTIONS.temporal; const chosen = temporals[Math.floor(Math.random() * temporals.length)]; // Insérer après le premier mot de la phrase const words = sentences[targetIndex].split(' '); // ✅ VALIDATION: Ne pas insérer dans expressions fixes ou comparatifs const firstWords = words.slice(0, 3).join(' ').toLowerCase(); const forbiddenPatterns = ['plus la', 'plus le', 'en effet', 'leur ', 'c\'est']; const isForbidden = forbiddenPatterns.some(pattern => firstWords.includes(pattern)); if (words.length > 5 && !isForbidden) { // ✅ Augmenté de 3→5 mots minimum words.splice(1, 0, chosen + ','); sentences[targetIndex] = words.join(' '); insertions++; logSh(` 🕐 Insertion temporelle: "${chosen}"`, 'DEBUG'); } } // Insertion nuance (dernier tiers) if (Math.random() < config.intensity * 0.6 && insertions < config.maxInsertions) { const targetIndex = Math.floor(sentences.length * 2 / 3); const nuances = MICRO_INSERTIONS.nuance; const chosen = nuances[Math.floor(Math.random() * nuances.length)]; // Insérer après une virgule existante if (sentences[targetIndex].includes(',')) { sentences[targetIndex] = sentences[targetIndex].replace(',', `, ${chosen},`); insertions++; logSh(` 💭 Insertion nuance: "${chosen}"`, 'DEBUG'); } } modified = sentences.join('. '); } catch (error) { logSh(`⚠️ Erreur micro-insertions: ${error.message}`, 'WARNING'); return { content: text, insertions: 0 }; } return { content: modified, insertions }; } /** * APPLICATION VARIATIONS PONCTUATION * @param {string} text - Texte à ponctuer * @param {object} options - { intensity, maxVariations } * @returns {object} - { content, variations } */ function applyPunctuationVariations(text, options = {}) { const config = { intensity: 0.2, maxVariations: 2, ...options }; if (!text || text.trim().length === 0) { return { content: text, variations: 0 }; } let modified = text; let variations = 0; try { PUNCTUATION_PATTERNS.forEach(punctPattern => { if (variations >= config.maxVariations) return; const matches = modified.match(punctPattern.pattern); if (matches && Math.random() < (config.intensity * punctPattern.probability)) { // Appliquer UNE SEULE fois (première occurrence) modified = modified.replace(punctPattern.pattern, punctPattern.replacement); variations++; logSh(` 📍 Ponctuation: ${punctPattern.description}`, 'DEBUG'); } }); } catch (error) { logSh(`⚠️ Erreur variations ponctuation: ${error.message}`, 'WARNING'); return { content: text, variations: 0 }; } return { content: modified, variations }; } /** * BINÔMES COURANTS À PRÉSERVER * Paires de mots qui doivent rester ensemble */ const COMMON_BINOMES = [ // Binômes avec "et" 'esthétique et praticité', 'esthétique et pratique', 'style et durabilité', 'design et fonctionnalité', 'élégance et performance', 'qualité et prix', 'rapidité et efficacité', 'simplicité et efficacité', 'confort et sécurité', 'robustesse et légèreté', 'durabilité et résistance', 'performance et fiabilité', 'innovation et tradition', 'modernité et authenticité', 'sur mesure et fiable', 'faciles à manipuler et à installer', // ✅ NOUVEAU: Compléments de nom 'son éclat et sa lisibilité', 'son éclat et sa', 'sa lisibilité et son', 'votre adresse et votre', 'leur durabilité et leur', 'notre gamme et nos', // ✅ NOUVEAU: Couples nom + complément descriptif 'personnalisation et élégance', 'qualité et performance', 'résistance et esthétique', 'praticité et design', 'fonctionnalité et style', 'efficacité et confort', 'solidité et légèreté', 'authenticité et modernité' ]; /** * PATTERNS REGEX POUR DÉTECTER COMPLÉMENTS DE NOM */ const COMPLEMENT_PATTERNS = [ // Possessifs + nom + et + possessif + nom /\b(son|sa|ses|votre|vos|leur|leurs|notre|nos)\s+\w+\s+et\s+(son|sa|ses|votre|vos|leur|leurs|notre|nos)\s+\w+\b/gi, // Nom abstrait + et + nom abstrait /\b(personnalisation|durabilité|résistance|esthétique|élégance|qualité|performance|praticité|fonctionnalité|efficacité|solidité|authenticité|modernité)\s+et\s+(personnalisation|durabilité|résistance|esthétique|élégance|qualité|performance|praticité|fonctionnalité|efficacité|solidité|authenticité|modernité)\b/gi ]; /** * VALIDATION BINÔMES * Vérifie si une partie de texte contient un binôme à préserver (liste + regex) */ function containsBinome(text) { const lowerText = text.toLowerCase(); // 1. Vérifier liste statique const hasStaticBinome = COMMON_BINOMES.some(binome => lowerText.includes(binome.toLowerCase()) ); if (hasStaticBinome) { return true; } // 2. Vérifier patterns regex dynamiques const hasDynamicPattern = COMPLEMENT_PATTERNS.some(pattern => { pattern.lastIndex = 0; return pattern.test(text); }); return hasDynamicPattern; } /** * RESTRUCTURATION LÉGÈRE * Découpage/fusion très occasionnel (probabilité faible) * @param {string} text - Texte à restructurer * @param {object} options - { intensity, maxRestructures } * @returns {object} - { content, restructures } */ function applyLightRestructuring(text, options = {}) { const config = { intensity: 0.2, maxRestructures: 1, // Maximum 1 restructuration ...options }; if (!text || text.trim().length === 0) { return { content: text, restructures: 0 }; } let modified = text; let restructures = 0; try { const sentences = modified.split('. '); // DÉCOUPAGE : Si une phrase très longue existe (>150 chars) if (Math.random() < config.intensity * 0.5 && restructures < config.maxRestructures) { for (let i = 0; i < sentences.length; i++) { if (sentences[i].length > 150) { // Chercher un point de découpe naturel const cutPoints = [ { pattern: /, car (.+)/, replacement: '. En effet, $1', connector: 'car' }, { pattern: /, donc (.+)/, replacement: '. Ainsi, $1', connector: 'donc' }, { pattern: / et (.{30,})/, replacement: '. Également, $1', connector: 'et long' } ]; for (const cutPoint of cutPoints) { if (sentences[i].match(cutPoint.pattern)) { sentences[i] = sentences[i].replace(cutPoint.pattern, cutPoint.replacement); restructures++; logSh(` ✂️ Découpage léger: "${cutPoint.connector}"`, 'DEBUG'); break; } } break; // Une seule restructuration } } } // FUSION : Si deux phrases très courtes consécutives (<40 chars chacune) if (Math.random() < config.intensity * 0.4 && restructures < config.maxRestructures) { for (let i = 0; i < sentences.length - 1; i++) { const current = sentences[i]; const next = sentences[i + 1]; if (current.length < 40 && next && next.length < 50) { // ✅ VALIDATION: Ne pas fusionner si binôme détecté const combined = current + ' ' + next; if (containsBinome(combined)) { logSh(` ⚠️ Fusion évitée: binôme détecté`, 'DEBUG'); continue; } // Fusion avec connecteur neutre + originaux const connectors = [', et', ', puis', ' ;', ', tout en', ', sans oublier', ', qui plus est,']; const connector = connectors[Math.floor(Math.random() * connectors.length)]; sentences[i] = current + connector + ' ' + next.toLowerCase(); sentences.splice(i + 1, 1); restructures++; logSh(` 🔗 Fusion légère: "${connector}"`, 'DEBUG'); break; // Une seule restructuration } } } modified = sentences.join('. '); } catch (error) { logSh(`⚠️ Erreur restructuration légère: ${error.message}`, 'WARNING'); return { content: text, restructures: 0 }; } return { content: modified, restructures }; } /** * APPLICATION COMPLÈTE MICRO-ENHANCEMENTS * Combine insertions + ponctuation + restructuration légère * @param {string} text - Texte à améliorer * @param {object} options - Options globales * @returns {object} - { content, stats } */ function applyMicroEnhancements(text, options = {}) { const config = { intensity: 0.3, enableInsertions: true, enablePunctuation: true, enableRestructuring: true, ...options }; if (!text || text.trim().length === 0) { return { content: text, stats: { insertions: 0, punctuations: 0, restructures: 0 } }; } let modified = text; const stats = { insertions: 0, punctuations: 0, restructures: 0, total: 0 }; try { // 1. Micro-insertions (si activé) if (config.enableInsertions) { const insertResult = applyMicroInsertions(modified, { intensity: config.intensity, maxInsertions: 2 }); modified = insertResult.content; stats.insertions = insertResult.insertions; } // 2. Variations ponctuation (si activé) - AVANT restructuration pour préserver patterns if (config.enablePunctuation) { const punctResult = applyPunctuationVariations(modified, { intensity: config.intensity * 1.5, // ✅ Augmenté 0.8 → 1.5 pour plus de chances maxVariations: 1 }); modified = punctResult.content; stats.punctuations = punctResult.variations; } // 3. Restructuration légère (si activé) if (config.enableRestructuring) { const restructResult = applyLightRestructuring(modified, { intensity: config.intensity * 0.6, maxRestructures: 1 }); modified = restructResult.content; stats.restructures = restructResult.restructures; } stats.total = stats.insertions + stats.punctuations + stats.restructures; // ✅ NETTOYAGE FINAL : Corriger espaces parasites avant ponctuation modified = modified .replace(/\s+\./g, '.') // Espace avant point .replace(/\s+,/g, ',') // Espace avant virgule .replace(/\s+;/g, ';') // Espace avant point-virgule .replace(/\s+:/g, ':') // Espace avant deux-points .replace(/\.\s+\./g, '. ') // Double points .replace(/\s+/g, ' ') // Multiples espaces .trim(); } catch (error) { logSh(`❌ Erreur micro-enhancements: ${error.message}`, 'WARNING'); return { content: text, stats }; } return { content: modified, stats }; } // ============= EXPORTS ============= module.exports = { applyMicroEnhancements, applyMicroInsertions, applyPunctuationVariations, applyLightRestructuring, MICRO_INSERTIONS, PUNCTUATION_PATTERNS };