confluent/scripts/audit-lexique.js
StillHammer 4236232a62 Refonte complète du lexique Confluent et système d'audit
- Correction et extension du lexique: 78 → 0 erreurs
- Ajout de 14 racines manquantes (toli, konu, aika, vito, paka, nutu, tuli, nemu, zeru, novi, muta, supu, selu, saki)
- Extension du lexique: 67 racines standards (53 → 67)
- Création de 6 nouveaux fichiers lexique (navigation, architecture, concepts philosophiques, étrangers, actions militaires, vêtements)
- Réduction consonnes rares: 26.5% → 2.7%
- Remplacement racines anglaises par finno-basques (malo→paka, situ→tuli, taki→kanu, time→aika)
- Correction des mots mal formés (ulak→kulak, koliukitan→koliukita, ulapisu→lapis, pekikayo→pekikazo)
- Amélioration script d'audit: charge maintenant verbes, compositions et grammaire (638 racines)
- Ajout scripts de maintenance (audit, correction consonnes rares, détection doublons)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 21:53:03 +08:00

399 lines
12 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Script d'audit du lexique Confluent
* Vérifie que tous les mots respectent les règles linguistiques
*/
const fs = require('fs');
const path = require('path');
const LEXIQUE_DIR = path.join(__dirname, '../ancien-confluent/lexique');
// Règles phonétiques
const CONSONNES_STANDARD = ['b', 'k', 'l', 'm', 'n', 'p', 's', 't', 'v', 'z'];
const CONSONNES_RARES = ['r', 'd', 'h', 'g']; // Sons "bruts" à éviter mais tolérés
const CONSONNES_INTERDITES = ['c', 'f', 'j', 'q', 'w', 'x', 'y'];
const VOYELLES_ACTIVES = ['a', 'e', 'i', 'o', 'u'];
const VOYELLES_RESERVEES = ['y', 'é', 'è'];
// Les 16 liaisons sacrées
const LIAISONS_SACREES = [
'i', 'ie', 'ii', 'iu', // I - Agentivité
'u', 'ui', // U - Appartenance
'a', 'aa', 'ae', 'ao', // A - Relation
'o', 'oa', // O - Tension
'e', 'ei', 'ea', 'eo' // E - Dimension
];
let errors = [];
let warnings = [];
let stats = {
total_mots: 0,
racines_sacrees: 0,
racines_standards: 0,
compositions: 0,
erreurs: 0,
avertissements: 0,
consonnes_rares_utilisees: 0,
mots_avec_consonnes_rares: []
};
/**
* Vérifie si un caractère est une consonne valide
*/
function estConsonneValide(c) {
return CONSONNES_STANDARD.includes(c) || CONSONNES_RARES.includes(c);
}
/**
* Vérifie si un caractère est une consonne rare
*/
function estConsonneRare(c) {
return CONSONNES_RARES.includes(c);
}
/**
* Vérifie si un caractère est une consonne interdite
*/
function estConsonneInterdite(c) {
return CONSONNES_INTERDITES.includes(c);
}
/**
* Vérifie si un caractère est une voyelle active
*/
function estVoyelleActive(c) {
return VOYELLES_ACTIVES.includes(c);
}
/**
* Vérifie si un caractère est une voyelle réservée
*/
function estVoyelleReservee(c) {
return VOYELLES_RESERVEES.includes(c);
}
/**
* Vérifie le format d'un mot (CV pour racines, CVCVC pour verbes)
*/
function verifierFormatCV(mot, file, motFr, type) {
const chars = mot.split('');
let hasConsonneRare = false;
// Vérifier les espaces (invalides)
if (mot.includes(' ')) {
errors.push(`[${file}] "${motFr}" → "${mot}": Caractère invalide ' '`);
return false;
}
// Vérifier les caractères invalides
for (let i = 0; i < chars.length; i++) {
const c = chars[i];
if (c === '-') continue; // Tirets OK pour compositions
if (estConsonneRare(c)) {
hasConsonneRare = true;
} else if (estConsonneInterdite(c)) {
errors.push(`[${file}] "${motFr}" → "${mot}": Consonne interdite '${c}'`);
return false;
} else if (estVoyelleReservee(c)) {
errors.push(`[${file}] "${motFr}" → "${mot}": Voyelle réservée '${c}' (y, é, è interdits)`);
return false;
} else if (!estConsonneValide(c) && !estVoyelleActive(c)) {
errors.push(`[${file}] "${motFr}" → "${mot}": Caractère invalide '${c}'`);
return false;
}
}
// Compter les consonnes rares
if (hasConsonneRare) {
stats.consonnes_rares_utilisees++;
stats.mots_avec_consonnes_rares.push({mot, file, motFr});
}
// Retirer les tirets (pour les compositions)
const motSansTirets = mot.replace(/-/g, '');
if (motSansTirets.length < 2) {
errors.push(`[${file}] "${motFr}" → "${mot}": Trop court (minimum 2 caractères)`);
return false;
}
const avantDernier = motSansTirets[motSansTirets.length - 2];
const dernier = motSansTirets[motSansTirets.length - 1];
// Les VERBES finissent par CVCVC (consonne finale)
// Les RACINES finissent par CV (voyelle finale)
if (type === 'verbe' || type === 'verbe_irregulier') {
// Verbes: structure CVCVC (5 lettres, finit par consonne)
if (!estConsonneValide(dernier)) {
errors.push(`[${file}] "${motFr}" → "${mot}": Verbe doit finir par consonne (CVCVC), mais dernier caractère '${dernier}' n'est pas une consonne`);
return false;
}
} else {
// Racines et compositions: finissent par CV (voyelle)
if (!estConsonneValide(avantDernier)) {
errors.push(`[${file}] "${motFr}" → "${mot}": Doit finir par CV (consonne+voyelle), mais avant-dernier caractère '${avantDernier}' n'est pas une consonne`);
return false;
}
if (!estVoyelleActive(dernier)) {
errors.push(`[${file}] "${motFr}" → "${mot}": Doit finir par CV (consonne+voyelle), mais dernier caractère '${dernier}' n'est pas une voyelle`);
return false;
}
}
return true;
}
/**
* Vérifie si une racine est sacrée (commence par voyelle)
*/
function estRacineSacree(mot) {
return estVoyelleActive(mot[0]);
}
/**
* Charge toutes les racines du lexique
*/
function chargerToutesLesRacines() {
const racines = new Map();
const files = fs.readdirSync(LEXIQUE_DIR).filter(f => f.endsWith('.json'));
files.forEach(file => {
const filePath = path.join(LEXIQUE_DIR, file);
const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (!content.dictionnaire) return;
Object.entries(content.dictionnaire).forEach(([motFr, data]) => {
if (!data.traductions) return;
data.traductions.forEach(trad => {
// Charger les racines explicites
if (trad.type === 'racine' || trad.type === 'racine_sacree') {
racines.set(trad.confluent, {
mot_fr: motFr,
forme_liee: trad.forme_liee,
file: file
});
}
// Charger les VERBES eux-mêmes comme racines (milak, kitan, etc.)
if (trad.type === 'verbe') {
racines.set(trad.confluent, {
mot_fr: motFr,
forme_liee: trad.forme_liee,
file: file,
source: 'verbe'
});
// Charger aussi la racine du verbe si elle existe
if (trad.racine) {
racines.set(trad.racine, {
mot_fr: motFr,
forme_liee: trad.forme_liee,
file: file,
source: 'verbe_racine'
});
}
}
// Charger les COMPOSITIONS comme racines valides (uraakota, etc.)
if (trad.type === 'composition' || trad.type === 'racine_sacree_composee') {
racines.set(trad.confluent, {
mot_fr: motFr,
forme_liee: trad.forme_liee || trad.confluent.slice(0, -1),
file: file,
source: 'composition'
});
}
// Charger les mots grammaticaux (négation, particules, démonstratifs, etc.) comme racines valides
if (trad.type === 'negation' || trad.type === 'particule' || trad.type === 'interrogation' || trad.type === 'demonstratif') {
racines.set(trad.confluent, {
mot_fr: motFr,
forme_liee: trad.forme_liee || trad.confluent.slice(0, -1),
file: file,
source: 'grammaire'
});
}
});
});
});
return racines;
}
/**
* Vérifie une composition
*/
function verifierComposition(trad, file, motFr, toutesLesRacines) {
if (!trad.composition) {
warnings.push(`[${file}] "${motFr}" → "${trad.confluent}": Type 'composition' mais pas de champ 'composition'`);
return;
}
if (!trad.racines || trad.racines.length === 0) {
warnings.push(`[${file}] "${motFr}" → "${trad.confluent}": Type 'composition' mais pas de champ 'racines'`);
return;
}
// Vérifier que les racines existent
trad.racines.forEach(racine => {
if (!toutesLesRacines.has(racine)) {
errors.push(`[${file}] "${motFr}" → "${trad.confluent}": Utilise racine inexistante "${racine}"`);
}
});
// Vérifier le format de composition
const parties = trad.composition.split('-');
// Vérifier les liaisons (les parties avec 1-2 lettres entre les racines)
parties.forEach((partie, index) => {
if (partie.length <= 2 && index > 0 && index < parties.length - 1) {
// C'est probablement une liaison
if (!LIAISONS_SACREES.includes(partie) && partie !== 'u' && partie !== 'a' && partie !== 'i' && partie !== 'o' && partie !== 'e') {
warnings.push(`[${file}] "${motFr}" → "${trad.confluent}": Liaison "${partie}" dans composition "${trad.composition}" n'est pas une liaison sacrée standard`);
}
}
});
}
/**
* Vérifie la forme liée
*/
function verifierFormeLiee(trad, file, motFr) {
if (trad.type !== 'racine' && trad.type !== 'racine_sacree') {
return;
}
if (!trad.forme_liee) {
warnings.push(`[${file}] "${motFr}" → "${trad.confluent}": Racine sans 'forme_liee'`);
return;
}
// La forme liée devrait être la racine sans la dernière voyelle
const attendu = trad.confluent.slice(0, -1);
if (trad.forme_liee !== attendu) {
warnings.push(`[${file}] "${motFr}" → "${trad.confluent}": forme_liee="${trad.forme_liee}" devrait être "${attendu}"`);
}
}
/**
* Audit d'un fichier
*/
function auditerFichier(file, toutesLesRacines) {
const filePath = path.join(LEXIQUE_DIR, file);
const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (!content.dictionnaire) return;
Object.entries(content.dictionnaire).forEach(([motFr, data]) => {
if (!data.traductions) return;
data.traductions.forEach(trad => {
stats.total_mots++;
// Vérifier le format CV/CVCVC selon le type
if (!verifierFormatCV(trad.confluent, file, motFr, trad.type)) {
stats.erreurs++;
return;
}
// Vérifier le type de racine
if (trad.type === 'racine' || trad.type === 'racine_sacree') {
const estSacree = estRacineSacree(trad.confluent);
if (trad.type === 'racine_sacree' && !estSacree) {
errors.push(`[${file}] "${motFr}" → "${trad.confluent}": Marqué 'racine_sacree' mais commence par consonne`);
stats.erreurs++;
}
if (trad.type === 'racine' && estSacree) {
warnings.push(`[${file}] "${motFr}" → "${trad.confluent}": Marqué 'racine' mais commence par voyelle (devrait être 'racine_sacree')`);
stats.avertissements++;
}
if (estSacree) {
stats.racines_sacrees++;
} else {
stats.racines_standards++;
}
// Vérifier forme liée
verifierFormeLiee(trad, file, motFr);
}
// Vérifier les compositions
if (trad.type === 'composition' || trad.type === 'racine_sacree_composee') {
stats.compositions++;
verifierComposition(trad, file, motFr, toutesLesRacines);
}
});
});
}
/**
* Fonction principale
*/
function main() {
console.log('🔍 Audit du lexique Confluent\n');
// Charger toutes les racines d'abord
console.log('📖 Chargement des racines...');
const toutesLesRacines = chargerToutesLesRacines();
console.log(` ${toutesLesRacines.size} racines chargées\n`);
// Auditer chaque fichier (sauf 00-grammaire pour l'audit, mais on l'a chargé pour les racines)
const files = fs.readdirSync(LEXIQUE_DIR)
.filter(f => f.endsWith('.json') && !f.startsWith('00-grammaire'));
console.log('🔎 Audit des fichiers...\n');
files.forEach(file => {
auditerFichier(file, toutesLesRacines);
});
// Vérifier le ratio de consonnes rares
const ratioConsonnesRares = (stats.consonnes_rares_utilisees / stats.total_mots) * 100;
if (ratioConsonnesRares > 10) {
warnings.push(`⚠️ ATTENTION: ${ratioConsonnesRares.toFixed(1)}% des mots utilisent des consonnes rares (r, d, h, g). Recommandé: <10%`);
}
// Afficher les résultats
console.log('\n📊 STATISTIQUES:\n');
console.log(` Total de mots vérifiés: ${stats.total_mots}`);
console.log(` Racines sacrées: ${stats.racines_sacrees} (${Math.round(stats.racines_sacrees / (stats.racines_sacrees + stats.racines_standards) * 100)}%)`);
console.log(` Racines standards: ${stats.racines_standards} (${Math.round(stats.racines_standards / (stats.racines_sacrees + stats.racines_standards) * 100)}%)`);
console.log(` Compositions: ${stats.compositions}`);
console.log(` Consonnes rares utilisées: ${stats.consonnes_rares_utilisees} (${ratioConsonnesRares.toFixed(1)}%)`);
console.log(` Erreurs: ${errors.length}`);
console.log(` Avertissements: ${warnings.length}`);
if (errors.length > 0) {
console.log('\n❌ ERREURS:\n');
errors.forEach(err => console.log(` ${err}`));
}
if (warnings.length > 0) {
console.log('\n⚠ AVERTISSEMENTS:\n');
warnings.forEach(warn => console.log(` ${warn}`));
}
if (errors.length === 0 && warnings.length === 0) {
console.log('\n✅ Aucune erreur détectée ! Le lexique est conforme.\n');
} else {
console.log('');
}
// Code de sortie
process.exit(errors.length > 0 ? 1 : 0);
}
if (require.main === module) {
main();
}
module.exports = { verifierFormatCV, estRacineSacree };