Ajout système raffinement LLM CF→FR + extension lexique nourriture

Nouvelles fonctionnalités:
- Endpoint /api/translate/conf2fr/llm avec raffinement LLM
- Prompt cf2fr-refinement.txt expliquant structure Confluent
- Test test-llm-refinement.bat pour validation

Améliorations lexique:
- Ajout lexique 23-nourriture.json (vocabulaire alimentaire)
- Ajout lexique 24-habitat.json (habitat et construction)
- Correction "generation" → noviuaita (nouvelles générations)
- Ajout "cuisiner" → mukunekas (composition muk-u-nekas)
- Fix Ariaska → Aliaska dans prompts

Tests et outils:
- Tests de coverage et reverse translation
- Convertisseur de nombres
- Debug lemmatisation

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
StillHammer 2025-12-01 11:40:30 +08:00
parent e93c2f485b
commit 5c03390aaf
28 changed files with 2439 additions and 105 deletions

View File

@ -7,12 +7,51 @@
* 3. Expansion sémantique (niveau 1: synonymes directs)
* 4. Calcul dynamique du nombre max d'entrées selon longueur
* 5. Fallback racines si aucun terme trouvé
* 6. Conversion automatique des nombres français Confluent
*/
const { convertFrenchNumber, isNumber } = require('./numberConverter');
/**
* FONCTION CENTRALE DE NORMALISATION
* Normalise un texte français: lowercase + ligatures + accents + contractions
* UTILISER CETTE FONCTION PARTOUT pour garantir la cohérence
*
* @param {string} text - Texte français à normaliser
* @returns {string} - Texte normalisé (sans accents, contractions expansées)
*/
function normalizeFrenchText(text) {
// ÉTAPE 1: Lowercase + ligatures
let result = text
.toLowerCase()
.replace(/œ/g, 'oe') // Ligature œ → oe (cœur → coeur)
.replace(/æ/g, 'ae'); // Ligature æ → ae
// ÉTAPE 2: Normaliser et retirer les accents
result = result
.normalize('NFD') // Décompose les caractères accentués
.replace(/[\u0300-\u036f]/g, ''); // Retire les diacritiques (é→e, è→e, ê→e, etc.)
// ÉTAPE 3: Expanser les contractions françaises (l', d', n', etc.)
// IMPORTANT: Capture TOUTES les apostrophes: ' (droite U+0027), ' (courbe gauche U+2018), ' (courbe droite U+2019)
result = result
.replace(/l[''']/g, 'le ') // l'enfant → le enfant
.replace(/d[''']/g, 'de ') // d'eau → de eau
.replace(/n[''']/g, 'ne ') // n'est → ne est
.replace(/j[''']/g, 'je ') // j'ai → je ai
.replace(/m[''']/g, 'me ') // m'a → me a
.replace(/t[''']/g, 'te ') // t'a → te a
.replace(/s[''']/g, 'se ') // s'est → se est
.replace(/c[''']/g, 'ce ') // c'est → ce est
.replace(/qu[''']/g, 'que '); // qu'il → que il
return result;
}
/**
* Tokenize un texte français
* - Utilise normalizeFrenchText() pour la normalisation
* - Détecte expressions figées (il y a, y a-t-il, etc.)
* - Lowercase
* - Retire ponctuation
* - Split sur espaces
* - Retire mots vides très courants (le, la, les, un, une, des, de, du)
@ -21,41 +60,24 @@
*/
function tokenizeFrench(text) {
// Mots vides à retirer (articles, prépositions très courantes)
// NOTE: Les pronoms personnels (je, tu, il, elle, etc.) ne sont PAS des stopwords
// car ils ont une traduction en Confluent (miki, sinu, tani, etc.)
const stopWords = new Set([
'le', 'la', 'les', 'un', 'une', 'des', 'de', 'du', 'd',
'au', 'aux', 'à', 'et', 'ou', 'où', 'est', 'sont',
'ne', 'je', 'me', 'te', 'se', 'ce', 'que', 'il', 'elle'
'au', 'aux', 'a', 'et', 'ou', 'ou', 'est', 'sont',
'ne', 'me', 'te', 'se', 'ce', 'que'
]);
// ÉTAPE 1: Normaliser et nettoyer le texte
// ORDRE IMPORTANT: lowercase → ligatures → accents → contractions
let processedText = text
.toLowerCase()
.replace(/œ/g, 'oe') // Ligature œ → oe (cœur → coeur)
.replace(/æ/g, 'ae') // Ligature æ → ae
.normalize('NFD') // Décompose les caractères accentués
.replace(/[\u0300-\u036f]/g, ''); // Retire les diacritiques (é→e, è→e, ê→e, etc.)
// ÉTAPE 1: Utiliser la fonction centrale de normalisation
let processedText = normalizeFrenchText(text);
// ÉTAPE 1.5: Nettoyer les contractions françaises (l', d', n', etc.)
// APRÈS normalisation pour que "l'été" devienne "l'ete" puis "le ete"
processedText = processedText
.replace(/l['']/g, 'le ') // l'enfant → le enfant
.replace(/d['']/g, 'de ') // d'eau → de eau
.replace(/n['']/g, 'ne ') // n'est → ne est
.replace(/j['']/g, 'je ') // j'ai → je ai
.replace(/m['']/g, 'me ') // m'a → me a
.replace(/t['']/g, 'te ') // t'a → te a
.replace(/s['']/g, 'se ') // s'est → se est
.replace(/c['']/g, 'ce ') // c'est → ce est
.replace(/qu['']/g, 'que '); // qu'il → que il
// Expressions existentielles → "exister"
// ÉTAPE 2: Expressions existentielles → "exister"
processedText = processedText
.replace(/il\s+y\s+a(?:vait)?/g, 'exister') // il y a, il y avait
.replace(/y\s+a-t-il/g, 'exister') // y a-t-il
.replace(/ne\s+y\s+a-t-il\s+pas/g, 'exister'); // n'y a-t-il pas (déjà décontracté)
// ÉTAPE 2: Tokenisation normale
// ÉTAPE 3: Tokenisation - retirer ponctuation et splitter
return processedText
.replace(/[^\w\s]/g, ' ') // Remplacer ponctuation par espaces
.split(/\s+/)
@ -252,6 +274,33 @@ function findRelevantEntries(words, lexique, maxEntries, normalizedText = '') {
// Chercher chaque mot
for (const word of words) {
// ÉTAPE 1: Vérifier si c'est un nombre
const numberConversion = convertFrenchNumber(word);
if (numberConversion) {
// C'est un nombre - créer une entrée virtuelle
const numberEntry = {
mot_francais: word,
traductions: [{
confluent: numberConversion.confluent,
type: numberConversion.type
}],
score: 1.0,
source_file: 'number_system'
};
foundEntries.set(word, numberEntry);
wordsFound.push({
input: word,
found: word,
confluent: numberConversion.confluent,
type: numberConversion.type,
score: 1.0
});
continue; // Passer au mot suivant
}
// ÉTAPE 2: Recherche normale dans le lexique
const results = searchWord(word, lexique.dictionnaire, normalizedText);
if (results.length > 0) {
@ -464,6 +513,7 @@ function analyzeContext(text, lexique, options = {}) {
}
module.exports = {
normalizeFrenchText, // FONCTION CENTRALE - utiliser partout !
tokenizeFrench,
calculateMaxEntries,
simpleLemmatize,

View File

@ -0,0 +1,109 @@
/**
* DEBUG COMPLET - PROBLÈME DE LEMMATISATION
*
* Objectif: Comprendre EXACTEMENT le traitement casse les mots accentués
*/
const { tokenizeFrench } = require('./contextAnalyzer');
console.log('\n' + '='.repeat(70));
console.log('DEBUG COMPLET - LEMMATISATION ACCENTS');
console.log('='.repeat(70) + '\n');
// Cas problématiques reportés
const problemes = [
'mémoire', // → m + moire
'échos', // → chos
'légume', // → l + gume
'épice', // → pice
'lumière', // → lumi + re
'fenêtre', // → fen + tre
];
console.log('1. TEST TOKENIZATION ACTUELLE:\n');
problemes.forEach(mot => {
const tokens = tokenizeFrench(mot);
const status = tokens.length === 1 && tokens[0].length > 2 ? '✅' : '❌';
console.log(` ${status} "${mot}" → [${tokens.map(t => `"${t}"`).join(', ')}]`);
});
console.log('\n' + '-'.repeat(70));
console.log('2. ANALYSE ÉTAPE PAR ÉTAPE:\n');
function analyzeStep(mot) {
console.log(`\n Mot: "${mot}"`);
console.log(` Code points: ${[...mot].map(c => 'U+' + c.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')).join(' ')}`);
// Étape 1: lowercase
let step1 = mot.toLowerCase();
console.log(` 1. toLowerCase: "${step1}"`);
// Étape 2: ligatures
let step2 = step1.replace(/œ/g, 'oe').replace(/æ/g, 'ae');
console.log(` 2. Ligatures: "${step2}"`);
// Étape 3: NFD normalize
let step3 = step2.normalize('NFD');
console.log(` 3. NFD: "${step3}" (length: ${step3.length})`);
console.log(` Code points: ${[...step3].map(c => 'U+' + c.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')).join(' ')}`);
// Étape 4: Retirer diacritiques
let step4 = step3.replace(/[\u0300-\u036f]/g, '');
console.log(` 4. Sans diacritiques: "${step4}"`);
// Étape 5: Contractions (ne devrait rien faire ici)
let step5 = step4
.replace(/l[''']/g, 'le ')
.replace(/d[''']/g, 'de ')
.replace(/n[''']/g, 'ne ')
.replace(/j[''']/g, 'je ')
.replace(/m[''']/g, 'me ')
.replace(/t[''']/g, 'te ')
.replace(/s[''']/g, 'se ')
.replace(/c[''']/g, 'ce ')
.replace(/qu[''']/g, 'que ');
console.log(` 5. Après contractions: "${step5}"`);
// Étape 6: Retirer ponctuation
let step6 = step5.replace(/[^\w\s]/g, ' ');
console.log(` 6. Sans ponctuation: "${step6}"`);
// Étape 7: Split
let step7 = step6.split(/\s+/).filter(w => w.length > 0);
console.log(` 7. Tokens: [${step7.map(t => `"${t}"`).join(', ')}]`);
return step7;
}
problemes.forEach(mot => analyzeStep(mot));
console.log('\n' + '-'.repeat(70));
console.log('3. TEST AVEC CONTEXTE (apostrophes):\n');
const contextTests = [
"l'échos",
"l'épice",
"la mémoire",
"les légumes",
"la lumière",
"la fenêtre"
];
contextTests.forEach(phrase => {
const tokens = tokenizeFrench(phrase);
console.log(` "${phrase}" → [${tokens.map(t => `"${t}"`).join(', ')}]`);
});
console.log('\n' + '-'.repeat(70));
console.log('4. VÉRIFICATION REGEX \\w AVEC ACCENTS:\n');
const testW = 'é';
console.log(` Le caractère "é" matche \\w ? ${/\w/.test(testW) ? 'OUI' : 'NON'}`);
console.log(` Le caractère "é" matche [^\\w\\s] ? ${/[^\w\s]/.test(testW) ? 'OUI (sera remplacé!)' : 'NON'}`);
const testE = 'e';
console.log(` Le caractère "e" matche \\w ? ${/\w/.test(testE) ? 'OUI' : 'NON'}`);
console.log('\n' + '='.repeat(70));
console.log('FIN DEBUG');
console.log('='.repeat(70) + '\n');

View File

@ -44,9 +44,9 @@ function loadLexiqueFromDir(lexiqueDir) {
try {
const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
if (content.dictionnaire) {
// Fusionner les entrées
for (const [motFr, data] of Object.entries(content.dictionnaire)) {
// Fonction helper pour merger des entrées
const mergeEntries = (entries, sectionName = 'dictionnaire') => {
for (const [motFr, data] of Object.entries(entries)) {
const key = normalizeText(motFr);
if (!result.dictionnaire[key]) {
@ -68,7 +68,8 @@ function loadLexiqueFromDir(lexiqueDir) {
if (!exists) {
result.dictionnaire[key].traductions.push({
...trad,
source_file: file
source_file: file,
source_section: sectionName
});
}
}
@ -98,6 +99,16 @@ function loadLexiqueFromDir(lexiqueDir) {
result.dictionnaire[key].source_files.push(file);
}
}
};
// Charger la section "dictionnaire" si elle existe
if (content.dictionnaire) {
mergeEntries(content.dictionnaire, 'dictionnaire');
}
// Charger la section "pronoms" si elle existe (pour 02-racines-standards.json)
if (content.pronoms) {
mergeEntries(content.pronoms, 'pronoms');
}
result.meta.files_loaded.push(file);

View File

@ -0,0 +1,121 @@
/**
* Convertisseur de nombres français Confluent (base 12)
*
* Système basé sur la documentation SYSTEME_NUMERIQUE_BASE12.md
*/
// Table de conversion pour les nombres de base (0-12)
const BASE_NUMBERS = {
// Mots français
'zero': { confluent: 'zaro', value: 0 },
'zéro': { confluent: 'zaro', value: 0 },
'un': { confluent: 'iko', value: 1 },
'une': { confluent: 'iko', value: 1 },
'deux': { confluent: 'diku', value: 2 },
'trois': { confluent: 'tiru', value: 3 },
'quatre': { confluent: 'katu', value: 4 },
'cinq': { confluent: 'penu', value: 5 },
'six': { confluent: 'seku', value: 6 },
'sept': { confluent: 'sivu', value: 7 },
'huit': { confluent: 'oktu', value: 8 },
'neuf': { confluent: 'novu', value: 9 },
'dix': { confluent: 'deku', value: 10 },
'onze': { confluent: 'levu', value: 11 },
'douze': { confluent: 'tolu', value: 12 },
// Nombres composés courants
'treize': { confluent: 'tolu iko', value: 13 },
'quatorze': { confluent: 'tolu diku', value: 14 },
'quinze': { confluent: 'tolu tiru', value: 15 },
'seize': { confluent: 'tolu katu', value: 16 },
// Dizaines
'vingt': { confluent: 'tolu oktu', value: 20 },
'trente': { confluent: 'diku tolu seku', value: 30 },
'quarante': { confluent: 'tiru tolu katu', value: 40 },
'cinquante': { confluent: 'katu tolu diku', value: 50 },
'soixante': { confluent: 'penu tolu', value: 60 },
// Centaines
'cent': { confluent: 'oktu tolu katu', value: 100 },
'mille': { confluent: 'seku seku tolu oktu', value: 1000 },
// Ordinaux
'premier': { confluent: 'ena', value: 1 },
'première': { confluent: 'ena', value: 1 },
'deuxième': { confluent: 'dikuena', value: 2 },
'second': { confluent: 'dikuena', value: 2 },
'seconde': { confluent: 'dikuena', value: 2 },
'troisième': { confluent: 'tiruena', value: 3 },
'dernier': { confluent: 'osiana', value: -1 },
'dernière': { confluent: 'osiana', value: -1 }
};
/**
* Convertit un nombre en français vers Confluent
* @param {string} frenchNumber - Nombre en français (normalisé, minuscules)
* @returns {object|null} - { confluent: string, value: number, type: 'nombre'|'ordinal' } ou null
*/
function convertFrenchNumber(frenchNumber) {
const normalized = frenchNumber.toLowerCase().trim();
if (BASE_NUMBERS[normalized]) {
const { confluent, value } = BASE_NUMBERS[normalized];
const type = ['premier', 'première', 'deuxième', 'second', 'seconde', 'troisième', 'dernier', 'dernière'].includes(normalized)
? 'ordinal'
: 'nombre';
return {
confluent,
value,
type,
score: 1.0
};
}
return null;
}
/**
* Détecte si un mot français est un nombre
* @param {string} word - Mot en français
* @returns {boolean}
*/
function isNumber(word) {
const normalized = word.toLowerCase().trim();
return BASE_NUMBERS.hasOwnProperty(normalized);
}
/**
* Convertit une liste de mots et identifie les nombres
* @param {string[]} words - Liste de mots français
* @returns {object[]} - Liste d'objets { word, confluent, isNumber, value }
*/
function convertNumbersInText(words) {
return words.map(word => {
const numberConversion = convertFrenchNumber(word);
if (numberConversion) {
return {
word,
confluent: numberConversion.confluent,
isNumber: true,
value: numberConversion.value,
type: numberConversion.type,
score: numberConversion.score
};
}
return {
word,
isNumber: false
};
});
}
module.exports = {
convertFrenchNumber,
isNumber,
convertNumbersInText,
BASE_NUMBERS
};

View File

@ -214,7 +214,7 @@ Les verbes sont des racines de 5 lettres (CVCVC) qui finissent par une consonne.
Castes:
- Nakukeko (Enfants des Échos): nak-u-keko
- Nakuura (Enfants du Courant): nak-u-ura
- Ariaska (Ailes-Grises): ar-i-aska
- Aliaska (Ailes-Grises): al-i-aska
- Akoazana (Faucons Chasseurs): ak-oa-zana
- Takitosa (Passes-bien): tak-i-tosa
- Oraumi (Voix de l'Aurore): or-a-umi

View File

@ -0,0 +1,89 @@
Tu es un expert linguiste spécialisé dans la traduction du Confluent (langue construite) vers le français.
# CONTEXTE DE LA LANGUE CONFLUENTE
La langue de la Confluence est une langue construite parlée par une civilisation de chamans vivant au confluent de deux rivières. Cette langue reflète leurs valeurs culturelles : observation, mémoire, transmission et liberté.
## Structure grammaticale (SOV - Sujet-Objet-Verbe)
L'ordre des mots est strictement : SUJET + OBJET + VERBE
**Particules grammaticales:**
- `va` = marqueur de SUJET
- `vo` = marqueur d'OBJET
- `no` = locatif (dans/sur/à)
- `na` = génitif (de/du/des - possession)
- `se` = coordination (et)
- `lo` = opposition/coordination (mais/et)
- `ta` = conjonction temporelle (tandis que/pendant que)
- `u` = marqueur de PRÉSENT
- `ul` = marqueur de CAPACITÉ/POSSIBILITÉ (pouvoir, devoir)
- `en` = marqueur de FUTUR
## Principes de composition
Les mots confluents sont souvent des compositions de racines avec des liaisons sacrées:
- `-i-` = agentivité (celui qui fait)
- `-u-` = appartenance (de/du)
- `-a-` = relation (avec)
- `-eo-` = totalité/éternité
**Exemples:**
- `nakukeko` = nak-u-keko = "enfants des échos"
- `aliaska` = al-i-aska = "porteurs de la grue libre" = Ailes-Grises
- `uraikota` = ura-i-kota = "confluence de vie" = civilisation
- `silimira` = sili-mira = "regard observant" = observation
## Castes et noms propres (toujours en minuscules)
- `nakukeko` = Enfants des Échos
- `nakuura` = Enfants du Courant
- `aliaska` = Ailes-Grises
- `akoazana` = Faucons Chasseurs
- `takitosa` = Passes-bien
- `oraumi` = Voix de l'Aurore
- `uraakota` = La Confluence (lieu sacré)
- `siliaska` = Le peuple (littéralement "regard libre")
## Concepts culturels importants
- **aska** = liberté (racine sacrée)
- **veri** = vérité
- **memu** = mémoire (concept central)
- **kota** = confluence/union
- **umi** = esprit/âme
- **aita** = ancêtres
# TA TÂCHE
On te donne une **traduction mot-à-mot brute** d'un texte confluent vers le français. Cette traduction contient:
1. Les particules grammaticales (va, vo, no, etc.)
2. Les traductions littérales de chaque mot
3. Les alternatives entre parenthèses
**Tu dois:**
1. Comprendre la structure grammaticale SOV
2. Identifier les particules et leur rôle
3. Reconstituer le sens en français fluide et naturel
4. Respecter le contexte culturel de la Confluence
5. Produire un texte français élégant et compréhensible
**Format de sortie:**
Retourne UNIQUEMENT le texte français final, sans explication ni métadonnées.
**Exemple:**
**Entrée brute:**
"va enfants des echos vo confluence voir u"
**Ta sortie:**
"Les Enfants des Échos observent la confluence."
---
**IMPORTANT:**
- Utilise les majuscules pour les noms propres de castes en français
- Garde le sens culturel et spirituel
- Transforme la structure SOV en structure française naturelle (SVO)
- Élimine les particules grammaticales confluentes (va, vo, no, etc.)
- Choisis la meilleure traduction parmi les alternatives proposées selon le contexte

View File

@ -566,6 +566,83 @@ app.post('/api/translate/conf2fr', (req, res) => {
}
});
// NEW: Confluent → French with LLM refinement
app.post('/api/translate/conf2fr/llm', async (req, res) => {
const { text, variant = 'ancien', provider = 'anthropic', model = 'claude-sonnet-4-20250514' } = req.body;
if (!text) {
return res.status(400).json({ error: 'Missing parameter: text' });
}
const variantKey = variant === 'proto' ? 'proto' : 'ancien';
if (!confluentIndexes[variantKey]) {
return res.status(500).json({ error: `Confluent index for ${variantKey} not loaded` });
}
try {
// Step 1: Get raw word-by-word translation
const rawTranslation = translateConfluentToFrench(text, confluentIndexes[variantKey]);
// Step 2: Load refinement prompt
const refinementPrompt = fs.readFileSync(path.join(__dirname, 'prompts', 'cf2fr-refinement.txt'), 'utf-8');
// Step 3: Use LLM to refine translation
let refinedText;
if (provider === 'anthropic') {
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
const message = await anthropic.messages.create({
model: model,
max_tokens: 2048,
system: refinementPrompt,
messages: [
{
role: 'user',
content: `Voici la traduction brute mot-à-mot du Confluent vers le français. Transforme-la en français fluide et naturel:\n\n${rawTranslation.translation}`
}
]
});
refinedText = message.content[0].text.trim();
} else if (provider === 'openai') {
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const completion = await openai.chat.completions.create({
model: model,
messages: [
{ role: 'system', content: refinementPrompt },
{ role: 'user', content: `Voici la traduction brute mot-à-mot du Confluent vers le français. Transforme-la en français fluide et naturel:\n\n${rawTranslation.translation}` }
]
});
refinedText = completion.choices[0].message.content.trim();
} else {
return res.status(400).json({ error: 'Unsupported provider. Use "anthropic" or "openai".' });
}
// Return both raw and refined versions
res.json({
confluentText: text,
rawTranslation: rawTranslation.translation,
refinedTranslation: refinedText,
wordsTranslated: rawTranslation.wordsTranslated,
wordsNotTranslated: rawTranslation.wordsNotTranslated,
provider,
model
});
} catch (error) {
console.error('Confluent→FR LLM refinement error:', error);
res.status(500).json({ error: error.message });
}
});
app.listen(PORT, () => {
console.log(`ConfluentTranslator running on http://localhost:${PORT}`);
console.log(`Loaded: ${lexiques.ancien?.meta?.total_entries || 0} ancien entries, ${lexiques.proto?.meta?.total_entries || 0} proto entries`);

View File

@ -0,0 +1,33 @@
const { loadAllLexiques } = require('./lexiqueLoader');
const path = require('path');
const baseDir = path.join(__dirname, '..');
const lexiques = loadAllLexiques(baseDir);
console.log('\n=== RECHERCHE CONCEPTS ABSTRAITS ===\n');
const recherches = [
'civilisation', 'observation', 'observer', 'regarder', 'regard',
'confluence', 'union', 'peuple', 'culture', 'tradition',
'sagesse', 'connaissance', 'memoire', 'mémoire', 'savoir',
'libre', 'liberte', 'liberté', 'voir', 'vision'
];
recherches.forEach(mot => {
// Normaliser comme le fait le lexiqueLoader
const normalized = mot.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
const found = lexiques.ancien.dictionnaire[normalized];
if (found?.traductions) {
console.log(`${mot}: ${found.traductions[0].confluent} (${found.traductions[0].type})`);
} else {
console.log(`${mot}: NON TROUVÉ`);
}
});
console.log('\n=== RACINES LIÉES (sili, aska, ura, kota) ===\n');
const racines = ['sili', 'aska', 'ura', 'kota'];
racines.forEach(mot => {
const found = lexiques.ancien.dictionnaire[mot];
if (found?.traductions) {
console.log(`${mot}: ${found.mot_francais} - ${found.traductions[0].confluent}`);
}
});

View File

@ -0,0 +1,69 @@
/**
* Test API avec caractères accentués
*/
const http = require('http');
const testText = "La mémoire des échos résonne dans la lumière. Les légumes parfument la fenêtre de notre civilisation.";
console.log('\n=== TEST API AVEC ACCENTS ===\n');
console.log('Texte envoyé:', testText);
console.log('');
const data = JSON.stringify({ text: testText });
const options = {
hostname: 'localhost',
port: 3000,
path: '/api/analyze/coverage',
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': Buffer.byteLength(data)
}
};
const req = http.request(options, (res) => {
let body = '';
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
const result = JSON.parse(body);
console.log('Couverture:', result.coverage + '%');
console.log('');
console.log('=== MOTS TROUVÉS ===');
result.found.forEach(w => {
console.log(` ✅ "${w.word}" → ${w.confluent}`);
});
console.log('');
console.log('=== MOTS MANQUANTS ===');
result.missing.forEach(w => {
console.log(` ❌ "${w.word}"`);
});
console.log('');
// Vérifier si les mots accentués sont cassés
const brokenWords = result.missing.filter(w =>
['m', 'moire', 'chos', 'lumi', 're', 'l', 'gumes', 'fen', 'tre'].includes(w.word)
);
if (brokenWords.length > 0) {
console.log('❌ PROBLÈME: Mots cassés détectés !');
console.log(' Les accents ne sont pas correctement traités.');
brokenWords.forEach(w => console.log(` - "${w.word}"`));
} else {
console.log('✅ OK: Aucun mot cassé par les accents !');
}
});
});
req.on('error', (e) => {
console.error('Erreur:', e.message);
console.log('Le serveur est-il en cours d\'exécution sur le port 3000 ?');
});
req.write(data);
req.end();

View File

@ -0,0 +1,34 @@
const { tokenizeFrench } = require('./contextAnalyzer');
console.log('\n=== TEST FIX APOSTROPHES ===\n');
const testCases = [
// Apostrophe droite (ASCII 39)
"l'échos",
"d'écouter",
"m'a dit",
"n'est pas",
// Apostrophe courbe (Unicode)
"l'enfant",
"d'eau",
// Mots avec accents
"mémoire",
"écouter",
"échos"
];
testCases.forEach(test => {
const tokens = tokenizeFrench(test);
console.log(`Input: "${test}"`);
console.log(`Tokens: [${tokens.map(t => `"${t}"`).join(', ')}]`);
console.log('');
});
console.log('\n=== TEST PHRASE COMPLÈTE ===\n');
const phrase = "Les échos de la mémoire résonnent dans l'esprit des anciens qui écoutent.";
const tokens = tokenizeFrench(phrase);
console.log(`Input: "${phrase}"`);
console.log(`Tokens: [${tokens.map(t => `"${t}"`).join(', ')}]`);
console.log(`Count: ${tokens.length} mots`);

View File

@ -0,0 +1,126 @@
@echo off
setlocal enabledelayedexpansion
echo ===============================================
echo CONFLUENT COVERAGE TEST SUITE
echo ===============================================
echo.
REM Kill any existing server on port 3000
echo [1/4] Arret du serveur existant...
for /f "tokens=5" %%a in ('netstat -ano ^| findstr :3000') do (
taskkill //F //PID %%a >nul 2>&1
)
timeout /t 2 /nobreak >nul
REM Start server in background
echo [2/4] Demarrage du serveur...
cd /d "%~dp0"
start /B cmd /c "npm start >nul 2>&1"
timeout /t 5 /nobreak >nul
REM Check if server is running
curl -s http://localhost:3000/api/stats >nul 2>&1
if errorlevel 1 (
echo [ERREUR] Le serveur n'a pas demarre correctement
exit /b 1
)
echo [3/4] Execution des tests de coverage...
echo.
REM Create temp files
set TEMP_FILE=%TEMP%\coverage_results.json
set TEMP_MISSING=%TEMP%\missing_words.txt
REM Test 1 - Texte court culturel
curl -s -X POST http://localhost:3000/api/analyze/coverage -H "Content-Type: application/json" -d "{\"text\": \"Les enfants des echos observent le courant dans la confluence\"}" > "%TEMP_FILE%"
for /f "tokens=2 delims=:," %%a in ('findstr "\"coverage\"" "%TEMP_FILE%"') do set COV1=%%a
echo === TEST 1 - Texte court culturel === > "%TEMP_MISSING%"
type "%TEMP_FILE%" | findstr /C:"\"missing\"" >> "%TEMP_MISSING%"
echo. >> "%TEMP_MISSING%"
REM Test 2 - Texte long culturel
curl -s -X POST http://localhost:3000/api/analyze/coverage -H "Content-Type: application/json" -d "{\"text\": \"La civilisation de la Confluence repose sur l'observation, la transmission de la memoire et l'union des castes. Les Enfants des Echos ecoutent les murmures du passe tandis que les Faucons Chasseurs protegent les frontieres.\"}" > "%TEMP_FILE%"
for /f "tokens=2 delims=:," %%a in ('findstr "\"coverage\"" "%TEMP_FILE%"') do set COV2=%%a
echo === TEST 2 - Texte long culturel === >> "%TEMP_MISSING%"
type "%TEMP_FILE%" | findstr /C:"\"missing\"" >> "%TEMP_MISSING%"
echo. >> "%TEMP_MISSING%"
REM Test 3 - Vocabulaire quotidien
curl -s -X POST http://localhost:3000/api/analyze/coverage -H "Content-Type: application/json" -d "{\"text\": \"manger boire eau nourriture pain viande poisson legume fruit sel epice cuire couteau table feu lumiere maison porte fenetre toit sol mur escalier\"}" > "%TEMP_FILE%"
for /f "tokens=2 delims=:," %%a in ('findstr "\"coverage\"" "%TEMP_FILE%"') do set COV3=%%a
echo === TEST 3 - Vocabulaire quotidien === >> "%TEMP_MISSING%"
type "%TEMP_FILE%" | findstr /C:"\"missing\"" >> "%TEMP_MISSING%"
echo. >> "%TEMP_MISSING%"
REM Test 4 - Pronoms et verbes
curl -s -X POST http://localhost:3000/api/analyze/coverage -H "Content-Type: application/json" -d "{\"text\": \"je tu il elle nous vous ils regarder voir observer ecouter parler dire penser savoir comprendre aimer vouloir pouvoir devoir faire aller venir\"}" > "%TEMP_FILE%"
for /f "tokens=2 delims=:," %%a in ('findstr "\"coverage\"" "%TEMP_FILE%"') do set COV4=%%a
echo === TEST 4 - Pronoms et verbes === >> "%TEMP_MISSING%"
type "%TEMP_FILE%" | findstr /C:"\"missing\"" >> "%TEMP_MISSING%"
echo. >> "%TEMP_MISSING%"
REM Test 5 - Adjectifs
curl -s -X POST http://localhost:3000/api/analyze/coverage -H "Content-Type: application/json" -d "{\"text\": \"grand petit haut bas long court chaud froid bon mauvais beau laid fort faible rapide lent clair sombre\"}" > "%TEMP_FILE%"
for /f "tokens=2 delims=:," %%a in ('findstr "\"coverage\"" "%TEMP_FILE%"') do set COV5=%%a
echo === TEST 5 - Adjectifs courants === >> "%TEMP_MISSING%"
type "%TEMP_FILE%" | findstr /C:"\"missing\"" >> "%TEMP_MISSING%"
echo. >> "%TEMP_MISSING%"
REM Test 6 - Nombres
curl -s -X POST http://localhost:3000/api/analyze/coverage -H "Content-Type: application/json" -d "{\"text\": \"un deux trois quatre cinq six sept huit neuf dix cent mille premier dernier\"}" > "%TEMP_FILE%"
for /f "tokens=2 delims=:," %%a in ('findstr "\"coverage\"" "%TEMP_FILE%"') do set COV6=%%a
echo === TEST 6 - Nombres === >> "%TEMP_MISSING%"
type "%TEMP_FILE%" | findstr /C:"\"missing\"" >> "%TEMP_MISSING%"
echo. >> "%TEMP_MISSING%"
REM Get stats
curl -s http://localhost:3000/api/stats > "%TEMP_FILE%"
for /f "tokens=2 delims=:," %%a in ('findstr "\"total_entries\"" "%TEMP_FILE%"') do set ENTRIES=%%a
echo [4/4] Generation du rapport...
echo.
echo ===============================================
echo RAPPORT DE COVERAGE
echo ===============================================
echo.
echo Lexique charge: %ENTRIES% entrees (ancien)
echo.
echo TEST 1 - Texte court culturel : %COV1%%%
echo TEST 2 - Texte long culturel : %COV2%%%
echo TEST 3 - Vocabulaire quotidien : %COV3%%%
echo TEST 4 - Pronoms et verbes : %COV4%%%
echo TEST 5 - Adjectifs courants : %COV5%%%
echo TEST 6 - Nombres : %COV6%%%
echo.
REM Calculate average
set /a AVG=(%COV1%+%COV2%+%COV3%+%COV4%+%COV5%+%COV6%)/6
echo COVERAGE MOYEN : %AVG%%%
echo.
echo ===============================================
echo MOTS MANQUANTS PAR TEST
echo ===============================================
echo.
REM Display missing words with python for better JSON parsing
python -c "import json; data=open(r'%TEMP_MISSING%', encoding='utf-8').read().replace('\\', '').replace('}{', '}\n{'); sections=data.split('==='); [print(section.strip()) for section in sections if section.strip()]" 2>nul
if errorlevel 1 (
echo [Parsage JSON echoue - affichage brut]
type "%TEMP_MISSING%"
)
echo.
echo ===============================================
echo.
echo Serveur toujours actif sur http://localhost:3000
echo Pour arreter: taskkill //F //PID [PID du node.exe]
echo.
REM Clean up temp files
del "%TEMP_FILE%" >nul 2>&1
del "%TEMP_MISSING%" >nul 2>&1
endlocal

View File

@ -0,0 +1,131 @@
#!/bin/bash
echo "==============================================="
echo " CONFLUENT COVERAGE TEST SUITE"
echo "==============================================="
echo ""
# Kill any existing server on port 3000
echo "[1/4] Arrêt du serveur existant..."
lsof -ti:3000 | xargs kill -9 2>/dev/null || true
sleep 2
# Start server in background
echo "[2/4] Démarrage du serveur..."
cd "$(dirname "$0")"
npm start > /dev/null 2>&1 &
SERVER_PID=$!
sleep 5
# Check if server is running
if ! curl -s http://localhost:3000/api/stats > /dev/null 2>&1; then
echo "[ERREUR] Le serveur n'a pas démarré correctement"
exit 1
fi
echo "[3/4] Exécution des tests de coverage..."
echo ""
# Function to extract coverage
get_coverage() {
echo "$1" | grep -o '"coverage":[0-9]*' | grep -o '[0-9]*'
}
# Function to extract missing words
get_missing() {
echo "$1" | python3 -c "import sys, json; data=json.load(sys.stdin); print(', '.join([w['word'] for w in data.get('missing', [])]))" 2>/dev/null || echo "N/A"
}
# Test 1 - Texte court culturel
RESULT1=$(curl -s -X POST http://localhost:3000/api/analyze/coverage \
-H "Content-Type: application/json" \
-d '{"text": "Les enfants des echos observent le courant dans la confluence"}')
COV1=$(get_coverage "$RESULT1")
MISS1=$(get_missing "$RESULT1")
# Test 2 - Texte long culturel
RESULT2=$(curl -s -X POST http://localhost:3000/api/analyze/coverage \
-H "Content-Type: application/json" \
-d '{"text": "La civilisation de la Confluence repose sur l'\''observation, la transmission de la memoire et l'\''union des castes. Les Enfants des Echos ecoutent les murmures du passe tandis que les Faucons Chasseurs protegent les frontieres."}')
COV2=$(get_coverage "$RESULT2")
MISS2=$(get_missing "$RESULT2")
# Test 3 - Vocabulaire quotidien
RESULT3=$(curl -s -X POST http://localhost:3000/api/analyze/coverage \
-H "Content-Type: application/json" \
-d '{"text": "manger boire eau nourriture pain viande poisson legume fruit sel epice cuire couteau table feu lumiere maison porte fenetre toit sol mur escalier"}')
COV3=$(get_coverage "$RESULT3")
MISS3=$(get_missing "$RESULT3")
# Test 4 - Pronoms et verbes
RESULT4=$(curl -s -X POST http://localhost:3000/api/analyze/coverage \
-H "Content-Type: application/json" \
-d '{"text": "je tu il elle nous vous ils regarder voir observer ecouter parler dire penser savoir comprendre aimer vouloir pouvoir devoir faire aller venir"}')
COV4=$(get_coverage "$RESULT4")
MISS4=$(get_missing "$RESULT4")
# Test 5 - Adjectifs
RESULT5=$(curl -s -X POST http://localhost:3000/api/analyze/coverage \
-H "Content-Type: application/json" \
-d '{"text": "grand petit haut bas long court chaud froid bon mauvais beau laid fort faible rapide lent clair sombre"}')
COV5=$(get_coverage "$RESULT5")
MISS5=$(get_missing "$RESULT5")
# Test 6 - Nombres
RESULT6=$(curl -s -X POST http://localhost:3000/api/analyze/coverage \
-H "Content-Type: application/json" \
-d '{"text": "un deux trois quatre cinq six sept huit neuf dix cent mille premier dernier"}')
COV6=$(get_coverage "$RESULT6")
MISS6=$(get_missing "$RESULT6")
# Get stats
STATS=$(curl -s http://localhost:3000/api/stats)
ENTRIES=$(echo "$STATS" | grep -o '"total_entries":[0-9]*' | head -1 | grep -o '[0-9]*')
echo "[4/4] Génération du rapport..."
echo ""
echo "==============================================="
echo " RAPPORT DE COVERAGE"
echo "==============================================="
echo ""
echo "Lexique chargé: $ENTRIES entrées (ancien)"
echo ""
echo "TEST 1 - Texte court culturel : ${COV1}%"
echo "TEST 2 - Texte long culturel : ${COV2}%"
echo "TEST 3 - Vocabulaire quotidien : ${COV3}%"
echo "TEST 4 - Pronoms et verbes : ${COV4}%"
echo "TEST 5 - Adjectifs courants : ${COV5}%"
echo "TEST 6 - Nombres : ${COV6}%"
echo ""
# Calculate average
AVG=$(( (COV1 + COV2 + COV3 + COV4 + COV5 + COV6) / 6 ))
echo "COVERAGE MOYEN : ${AVG}%"
echo ""
echo "==============================================="
echo " MOTS MANQUANTS PAR TEST"
echo "==============================================="
echo ""
echo "TEST 1 (${COV1}%):"
echo " $MISS1"
echo ""
echo "TEST 2 (${COV2}%):"
echo " $MISS2"
echo ""
echo "TEST 3 (${COV3}%):"
echo " $MISS3"
echo ""
echo "TEST 4 (${COV4}%):"
echo " $MISS4"
echo ""
echo "TEST 5 (${COV5}%):"
echo " $MISS5"
echo ""
echo "TEST 6 (${COV6}%):"
echo " $MISS6"
echo ""
echo "==============================================="
echo ""
echo "Serveur toujours actif sur http://localhost:3000 (PID: $SERVER_PID)"
echo "Pour arrêter: kill $SERVER_PID"
echo ""

View File

@ -0,0 +1,42 @@
const { loadAllLexiques } = require('./lexiqueLoader');
const { analyzeContext } = require('./contextAnalyzer');
const path = require('path');
const baseDir = path.join(__dirname, '..');
const lexiques = loadAllLexiques(baseDir);
console.log('\n=== TEST TEXTE CULTUREL ===\n');
const texteTest = `
La civilisation de la Confluence est fondée sur l'observation et la mémoire.
Notre culture valorise la liberté du regard et la connaissance transmise par les ancêtres.
Les Siliaska, peuple du regard libre, préservent la sagesse de la tradition.
L'union fait notre force, et la confluence de nos savoirs nous guide.
Nous observons le monde avec attention pour voir la vérité.
`;
const result = analyzeContext(texteTest, lexiques.ancien);
console.log(`Texte: ${result.metadata.wordCount} mots\n`);
console.log(`Couverture: ${result.metadata.coveragePercent}%`);
console.log(`Trouvés: ${result.metadata.wordsFound.length} / Manquants: ${result.metadata.wordsNotFound.length}\n`);
console.log('=== CONCEPTS TROUVÉS ===\n');
const concepts = ['civilisation', 'confluence', 'observation', 'memoire', 'culture',
'liberte', 'regard', 'connaissance', 'ancetres', 'peuple',
'sagesse', 'tradition', 'union', 'savoir', 'observer', 'voir'];
result.metadata.wordsFound.forEach(w => {
if (concepts.some(c => w.input.includes(c) || w.found.includes(c))) {
console.log(`✅ "${w.input}" → ${w.confluent} (${w.type})`);
}
});
console.log('\n=== MOTS MANQUANTS ===');
if (result.metadata.wordsNotFound.length > 0) {
console.log(result.metadata.wordsNotFound.join(', '));
} else {
console.log('Aucun !');
}
console.log(`\n📊 Taux de couverture final: ${result.metadata.coveragePercent}%`);

View File

@ -0,0 +1,34 @@
const { loadAllLexiques } = require('./lexiqueLoader');
const path = require('path');
const baseDir = path.join(__dirname, '..');
const lexiques = loadAllLexiques(baseDir);
console.log('\n=== RECHERCHE RACINES POUR COMPOSITIONS ===\n');
// Rechercher les racines nécessaires
const recherches = [
'manger', 'nourriture', 'eau', 'boire', 'pierre', 'lumiere', 'abri',
'monter', 'ouvrir', 'ouverture', 'place', 'plante', 'aromate',
'gouffre', 'humide'
];
recherches.forEach(mot => {
const found = lexiques.ancien.dictionnaire[mot];
if (found && found.traductions) {
console.log(`${mot}: ${found.traductions[0].confluent} (${found.traductions[0].type})`);
} else {
console.log(`${mot}: NON TROUVÉ`);
}
});
console.log('\n=== VÉRIFIER EXISTANTS ===\n');
const aVerifier = ['manque', 'boire', 'fruit', 'legume', 'aromate', 'table', 'lumiere', 'fenetre', 'toit', 'mur', 'escalier'];
aVerifier.forEach(mot => {
const found = lexiques.ancien.dictionnaire[mot];
if (found && found.traductions) {
console.log(`⚠️ ${mot} EXISTE DÉJÀ: ${found.traductions[0].confluent}`);
} else {
console.log(`${mot}: À CRÉER`);
}
});

View File

@ -0,0 +1,28 @@
const { simpleLemmatize } = require('./contextAnalyzer');
const { loadAllLexiques } = require('./lexiqueLoader');
const path = require('path');
const baseDir = path.join(__dirname, '..');
const lexiques = loadAllLexiques(baseDir);
console.log('\n=== TEST LEMMATISATION NOUVEAUX MOTS ===\n');
const tests = ['fruits', 'légumes', 'aromates', 'monte', 'ouvrons', 'murs', 'toits'];
tests.forEach(mot => {
const lemmas = simpleLemmatize(mot);
console.log(`${mot} → lemmes: [${lemmas.join(', ')}]`);
let found = false;
for (const lemma of lemmas) {
const entry = lexiques.ancien.dictionnaire[lemma];
if (entry?.traductions) {
console.log(` ✅ trouvé: ${lemma}${entry.traductions[0].confluent}`);
found = true;
break;
}
}
if (!found) {
console.log(' ❌ NON TROUVÉ');
}
console.log('');
});

View File

@ -0,0 +1,109 @@
@echo off
setlocal enabledelayedexpansion
echo ===============================================
echo CONFLUENT LLM REFINEMENT TEST
echo (CF to FR with AI improvement)
echo ===============================================
echo.
REM Kill any existing server on port 3000
echo [1/3] Arret du serveur existant...
for /f "tokens=5" %%a in ('netstat -ano ^| findstr :3000') do (
taskkill //F //PID %%a >nul 2>&1
)
timeout /t 2 /nobreak >nul
REM Start server in background
echo [2/3] Demarrage du serveur...
cd /d "%~dp0"
start /B cmd /c "npm start >nul 2>&1"
timeout /t 5 /nobreak >nul
REM Check if server is running
curl -s http://localhost:3000/api/stats >nul 2>&1
if errorlevel 1 (
echo [ERREUR] Le serveur n'a pas demarre correctement
exit /b 1
)
echo [3/3] Test de traduction avec LLM refinement...
echo.
REM Create temp file
set TEMP_FILE=%TEMP%\llm_refinement.json
REM ===============================================
REM TEXTE ORIGINAL EN FRANCAIS (reference):
REM "Les enfants des echos observent la confluence.
REM Ils ecoutent les murmures du passe et transmettent la memoire aux nouvelles generations.
REM Les faucons chasseurs protegent les frontieres tandis que les ailes-grises volent dans le ciel sombre.
REM La civilisation repose sur l'observation, la transmission et l'union des castes.
REM Nous devons comprendre et aimer notre peuple pour preserver la liberte et la verite."
REM ===============================================
REM Texte en Confluent a traduire
set "CF_TEXT=Va Nakukeko vo uraakota mirak u. Va tanisu vo temak vosak tikam u se vo memu no noviuaita kisun u. Va Akoazana vo bosa zakis u ta va Aliaska no kumu zeru aliuk u. Va uraikota no silimira se kisunuaita se kotauneki kota tokas u. Va mikisu vo na siliaska sekam ul se koris ul se vo aska se veri nekas u."
echo ===============================================
echo TEXTE CONFLUENT A TRADUIRE:
echo ===============================================
echo.
echo %CF_TEXT%
echo.
echo ===============================================
REM Call the LLM refinement API
echo.
echo Appel de l'API avec LLM (cela peut prendre quelques secondes)...
echo.
curl -s -X POST http://localhost:3000/api/translate/conf2fr/llm ^
-H "Content-Type: application/json" ^
-d "{\"text\": \"%CF_TEXT%\", \"provider\": \"anthropic\", \"model\": \"claude-sonnet-4-20250514\"}" > "%TEMP_FILE%"
REM Check if the request succeeded
if errorlevel 1 (
echo [ERREUR] La requete API a echoue
exit /b 1
)
echo ===============================================
echo RESULTAT DE LA TRADUCTION:
echo ===============================================
echo.
REM Parse and display the results
python -c "import json, sys; data=json.load(open(r'%TEMP_FILE%', encoding='utf-8')); print('=== TRADUCTION BRUTE (mot-a-mot) ==='); print(data.get('rawTranslation', 'N/A')[:500] + '...'); print(); print('=== TRADUCTION RAFFINEE (LLM) ==='); print(data.get('refinedTranslation', 'N/A')); print(); print('PROVIDER:', data.get('provider', 'N/A')); print('MODEL:', data.get('model', 'N/A')); print('MOTS TRADUITS:', data.get('wordsTranslated', 0)); print('MOTS NON TRADUITS:', data.get('wordsNotTranslated', 0))" 2>nul
if errorlevel 1 (
echo [Parsage JSON echoue - affichage brut]
type "%TEMP_FILE%"
)
echo.
echo ===============================================
echo COMPARAISON AVEC L'ORIGINAL:
echo ===============================================
echo.
echo ORIGINAL FR:
echo "Les enfants des echos observent la confluence.
echo Ils ecoutent les murmures du passe et transmettent
echo la memoire aux nouvelles generations. Les faucons
echo chasseurs protegent les frontieres tandis que les
echo ailes-grises volent dans le ciel sombre. La
echo civilisation repose sur l'observation, la transmission
echo et l'union des castes. Nous devons comprendre et aimer
echo notre peuple pour preserver la liberte et la verite."
echo.
echo ===============================================
echo.
echo Serveur toujours actif sur http://localhost:3000
echo Pour arreter: taskkill //F //PID [PID du node.exe]
echo.
REM Clean up temp file
del "%TEMP_FILE%" >nul 2>&1
endlocal

View File

@ -1,32 +1,55 @@
/**
* Test de couverture lexicale sur textes longs
* UTILISE LES FONCTIONS CENTRALISÉES de contextAnalyzer.js
*/
const fs = require('fs');
const path = require('path');
const { normalizeFrenchText, tokenizeFrench, simpleLemmatize } = require('./contextAnalyzer');
// Load all lexicon files
const lexiqueDir = path.join(__dirname, '../ancien-confluent/lexique');
const lexiqueFiles = fs.readdirSync(lexiqueDir).filter(f => f.endsWith('.json'));
const lexiqueFiles = fs.readdirSync(lexiqueDir).filter(f => f.endsWith('.json') && !f.startsWith('_'));
const fullLexique = new Map();
// Helper to normalize text (lowercase + strip accents)
function normalize(text) {
return text.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}
lexiqueFiles.forEach(file => {
const content = JSON.parse(fs.readFileSync(path.join(lexiqueDir, file), 'utf8'));
if (content.dictionnaire) {
Object.entries(content.dictionnaire).forEach(([key, value]) => {
// Add both original and normalized versions
fullLexique.set(key.toLowerCase(), value);
fullLexique.set(normalize(key), value);
try {
const content = JSON.parse(fs.readFileSync(path.join(lexiqueDir, file), 'utf8'));
if (value.synonymes_fr) {
value.synonymes_fr.forEach(syn => {
fullLexique.set(syn.toLowerCase(), value);
fullLexique.set(normalize(syn), value);
});
}
});
// Charger le dictionnaire principal
if (content.dictionnaire) {
Object.entries(content.dictionnaire).forEach(([key, value]) => {
// Normaliser la clé avec la fonction centrale
const normalizedKey = normalizeFrenchText(key).trim();
fullLexique.set(normalizedKey, value);
// Ajouter aussi les synonymes
if (value.synonymes_fr) {
value.synonymes_fr.forEach(syn => {
const normalizedSyn = normalizeFrenchText(syn).trim();
fullLexique.set(normalizedSyn, value);
});
}
});
}
// Charger aussi la section "pronoms" si elle existe
if (content.pronoms) {
Object.entries(content.pronoms).forEach(([key, value]) => {
const normalizedKey = normalizeFrenchText(key).trim();
fullLexique.set(normalizedKey, value);
if (value.synonymes_fr) {
value.synonymes_fr.forEach(syn => {
const normalizedSyn = normalizeFrenchText(syn).trim();
fullLexique.set(normalizedSyn, value);
});
}
});
}
} catch (error) {
console.error(`Erreur chargement ${file}:`, error.message);
}
});
@ -53,35 +76,35 @@ const longTexts = [
console.log('\n=== LONG TEXT COVERAGE TEST ===\n');
console.log(`Lexique size: ${fullLexique.size} entries\n`);
// Common French articles and prepositions to ignore in coverage
const stopWords = new Set(['le', 'la', 'les', 'un', 'une', 'des', 'du', 'de', 'au', 'aux', 'a', 'l', 'd', 'c', 's', 'n', 't', 'qu', 'j', 'm']);
let globalFound = 0;
let globalTotal = 0;
const allMissing = new Set();
longTexts.forEach(({ title, text }) => {
const words = text.toLowerCase()
.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
.replace(/['']/g, ' ')
.split(/[\s,;:.!?()«»""-]+/)
.filter(w => w.length > 0);
// Filter out stopwords before checking
const contentWords = words.filter(w => !stopWords.has(w));
// UTILISER LA FONCTION CENTRALE tokenizeFrench()
const words = tokenizeFrench(text);
const found = [];
const missing = [];
contentWords.forEach(word => {
words.forEach(word => {
// Chercher le mot directement
if (fullLexique.has(word)) {
found.push(word);
} else {
// Try lemmatization for -ment adverbs
const withoutMent = word.replace(/ment$/, '');
if (fullLexique.has(withoutMent)) {
found.push(word);
} else {
// Essayer avec la lemmatisation
const lemmas = simpleLemmatize(word);
let foundViaLemma = false;
for (const lemma of lemmas) {
if (fullLexique.has(lemma)) {
found.push(word);
foundViaLemma = true;
break;
}
}
if (!foundViaLemma) {
missing.push(word);
allMissing.add(word);
}
@ -89,12 +112,12 @@ longTexts.forEach(({ title, text }) => {
});
globalFound += found.length;
globalTotal += contentWords.length;
globalTotal += words.length;
const coverage = contentWords.length > 0 ? ((found.length / contentWords.length) * 100).toFixed(1) : 100;
const coverage = words.length > 0 ? ((found.length / words.length) * 100).toFixed(1) : 100;
const status = parseFloat(coverage) >= 95 ? '✅' : parseFloat(coverage) >= 70 ? '⚠️' : '❌';
console.log(`${status} ${coverage}% - ${title} (${found.length}/${contentWords.length} mots)`);
console.log(`${status} ${coverage}% - ${title} (${found.length}/${words.length} mots)`);
if (missing.length > 0) {
const uniqueMissing = [...new Set(missing)];
console.log(` Manquants (${uniqueMissing.length}): ${uniqueMissing.slice(0, 10).join(', ')}${uniqueMissing.length > 10 ? '...' : ''}`);
@ -110,11 +133,7 @@ console.log(`\n🔍 MOTS MANQUANTS UNIQUES: ${allMissing.size}\n`);
// Count frequency of missing words
const missingFrequency = new Map();
longTexts.forEach(({ text }) => {
const words = text.toLowerCase()
.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
.replace(/['']/g, ' ')
.split(/[\s,;:.!?()«»""-]+/)
.filter(w => w.length > 0 && !stopWords.has(w));
const words = tokenizeFrench(text);
words.forEach(word => {
if (allMissing.has(word)) {

View File

@ -0,0 +1,37 @@
const { loadAllLexiques } = require('./lexiqueLoader');
const { analyzeContext } = require('./contextAnalyzer');
const path = require('path');
const baseDir = path.join(__dirname, '..');
const lexiques = loadAllLexiques(baseDir);
console.log('\n=== TEST NOUVEAU VOCABULAIRE ===\n');
const texteTest = `
Les enfants montent l'escalier de pierre pour boire de l'eau fraîche.
Sur la table, il y a des fruits, des légumes et des aromates.
La lumière entre par la fenêtre et éclaire les murs.
Le toit protège la maison de la pluie.
Quand il manque de nourriture, nous ouvrons les réserves.
`;
const result = analyzeContext(texteTest, lexiques.ancien);
console.log(`Texte analysé: ${result.metadata.wordCount} mots\n`);
console.log(`Couverture: ${result.metadata.coveragePercent}%`);
console.log(`Trouvés: ${result.metadata.wordsFound.length} / Manquants: ${result.metadata.wordsNotFound.length}\n`);
console.log('=== MOTS TROUVÉS (nouveaux) ===\n');
const nouveaux = ['boire', 'fruit', 'legume', 'aromate', 'table', 'fenetre', 'toit', 'mur', 'escalier', 'manque', 'monter', 'ouvrir'];
result.metadata.wordsFound.forEach(w => {
if (nouveaux.includes(w.input) || nouveaux.includes(w.found)) {
console.log(`✅ "${w.input}" → ${w.confluent} (${w.type})`);
}
});
console.log('\n=== MOTS MANQUANTS ===\n');
if (result.metadata.wordsNotFound.length > 0) {
console.log(result.metadata.wordsNotFound.slice(0, 10).join(', '));
} else {
console.log('Aucun !');
}

View File

@ -0,0 +1,55 @@
const { tokenizeFrench, simpleLemmatize } = require('./contextAnalyzer');
const { loadAllLexiques } = require('./lexiqueLoader');
const path = require('path');
const baseDir = path.join(__dirname, '..');
const lexiques = loadAllLexiques(baseDir);
console.log('\n=== TEST SYSTÈME DE PRONOMS ===\n');
const tests = [
'je parle',
'tu parles',
'il parle',
'elle parle',
'nous parlons',
'vous parlez',
'ils parlent',
'elles parlent'
];
tests.forEach(phrase => {
const tokens = tokenizeFrench(phrase);
console.log(`"${phrase}" → tokens: [${tokens.join(', ')}]`);
tokens.forEach(token => {
const lemmas = simpleLemmatize(token);
const found = lexiques.ancien.dictionnaire[token];
if (found) {
const conf = found.traductions[0]?.confluent || '?';
console.log(` ✅ "${token}" → ${conf}`);
} else {
// Vérifier les lemmes
let foundLemma = false;
for (const lemma of lemmas) {
const entry = lexiques.ancien.dictionnaire[lemma];
if (entry) {
const conf = entry.traductions[0]?.confluent || '?';
console.log(` ✅ "${token}" (lemme: "${lemma}") → ${conf}`);
foundLemma = true;
break;
}
}
if (!foundLemma) {
console.log(` ❌ "${token}" → NON TROUVÉ`);
}
}
});
console.log('');
});
console.log('\n=== CONSTRUCTION PRONOMS PLURIELS ===\n');
console.log('nous = miki (je) + su (pluriel) = mikisu');
console.log('vous = sinu (tu) + su (pluriel) = sinusu');
console.log('ils/elles = tani (il/elle) + su (pluriel) = tanisu');

View File

@ -0,0 +1,51 @@
const { loadAllLexiques } = require('./lexiqueLoader');
const path = require('path');
const baseDir = path.join(__dirname, '..');
const lexiques = loadAllLexiques(baseDir);
console.log('\n=== RECHERCHE PRONOMS PERSONNELS ===\n');
const pronouns = ['je', 'tu', 'il', 'elle', 'nous', 'vous', 'ils', 'elles', 'on'];
pronouns.forEach(pronoun => {
const found = [];
for (const [key, entry] of Object.entries(lexiques.ancien.dictionnaire)) {
if (key.toLowerCase() === pronoun || entry.mot_francais?.toLowerCase() === pronoun) {
found.push({
key,
mot: entry.mot_francais,
traductions: entry.traductions
});
}
}
if (found.length > 0) {
console.log(`${pronoun}:`);
found.forEach(f => {
console.log(` - ${f.traductions?.map(t => t.confluent).join(', ') || 'N/A'}`);
console.log(` Type: ${f.traductions?.map(t => t.type).join(', ') || 'N/A'}`);
});
} else {
console.log(`${pronoun}: NON TROUVÉ`);
}
console.log('');
});
console.log('\n=== RECHERCHE TOUS LES MOTS CONTENANT "tanu" ou "tasu" ===\n');
for (const [key, entry] of Object.entries(lexiques.ancien.dictionnaire)) {
const hasTarget = entry.traductions?.some(t =>
t.confluent?.includes('tanu') || t.confluent?.includes('tasu')
);
if (hasTarget) {
console.log(`"${entry.mot_francais || key}":`);
entry.traductions.forEach(t => {
if (t.confluent?.includes('tanu') || t.confluent?.includes('tasu')) {
console.log(`${t.confluent} (${t.type})`);
}
});
}
}

View File

@ -0,0 +1,106 @@
@echo off
setlocal enabledelayedexpansion
echo ===============================================
echo CONFLUENT REVERSE TRANSLATION TEST
echo (Confluent to French)
echo ===============================================
echo.
REM Kill any existing server on port 3000
echo [1/3] Arret du serveur existant...
for /f "tokens=5" %%a in ('netstat -ano ^| findstr :3000') do (
taskkill //F //PID %%a >nul 2>&1
)
timeout /t 2 /nobreak >nul
REM Start server in background
echo [2/3] Demarrage du serveur...
cd /d "%~dp0"
start /B cmd /c "npm start >nul 2>&1"
timeout /t 5 /nobreak >nul
REM Check if server is running
curl -s http://localhost:3000/api/stats >nul 2>&1
if errorlevel 1 (
echo [ERREUR] Le serveur n'a pas demarre correctement
exit /b 1
)
echo [3/3] Test de traduction Confluent to Francais...
echo.
REM Create temp file
set TEMP_FILE=%TEMP%\reverse_translation.json
REM ===============================================
REM TEXTE ORIGINAL EN FRANCAIS (reference):
REM "Les enfants des echos observent la confluence.
REM Ils ecoutent les murmures du passe et transmettent la memoire aux nouvelles generations.
REM Les faucons chasseurs protegent les frontieres tandis que les ailes-grises volent dans le ciel sombre.
REM La civilisation repose sur l'observation, la transmission et l'union des castes.
REM Nous devons comprendre et aimer notre peuple pour preserver la liberte et la verite."
REM ===============================================
REM Texte en Confluent a traduire
set "CF_TEXT=Va Nakukeko vo uraakota mirak u. Va tanisu vo temak vosak tikam u se vo memu no noviuaita kisun u. Va Akoazana vo bosa zakis u ta va Aliaska no kumu zeru aliuk u. Va uraikota no silimira se kisunuaita se kotauneki kota tokas u. Va mikisu vo na siliaska sekam ul se koris ul se vo aska se veri nekas u."
echo ===============================================
echo TEXTE CONFLUENT A TRADUIRE:
echo ===============================================
echo.
echo %CF_TEXT%
echo.
echo ===============================================
REM Call the Confluent to French translation API
curl -s -X POST http://localhost:3000/api/translate/conf2fr ^
-H "Content-Type: application/json" ^
-d "{\"text\": \"%CF_TEXT%\"}" > "%TEMP_FILE%"
REM Check if the request succeeded
if errorlevel 1 (
echo [ERREUR] La requete API a echoue
exit /b 1
)
echo.
echo ===============================================
echo RESULTAT DE LA TRADUCTION:
echo ===============================================
echo.
REM Parse and display the translation
python -c "import json, sys; data=json.load(open(r'%TEMP_FILE%', encoding='utf-8')); print('TRADUCTION FRANCAISE:'); print('-' * 47); print(data.get('translation', 'N/A')); print(); print('CONFIANCE:', str(data.get('confidence', 0)) + '%%'); print('MOTS TRADUITS:', data.get('wordsTranslated', 0)); print('MOTS NON TRADUITS:', data.get('wordsNotTranslated', 0))" 2>nul
if errorlevel 1 (
echo [Parsage JSON echoue - affichage brut]
type "%TEMP_FILE%"
)
echo.
echo ===============================================
echo COMPARAISON AVEC L'ORIGINAL:
echo ===============================================
echo.
echo ORIGINAL FR:
echo "Les enfants des echos observent la confluence.
echo Ils ecoutent les murmures du passe et transmettent
echo la memoire aux nouvelles generations. Les faucons
echo chasseurs protegent les frontieres tandis que les
echo ailes-grises volent dans le ciel sombre. La
echo civilisation repose sur l'observation, la transmission
echo et l'union des castes. Nous devons comprendre et aimer
echo notre peuple pour preserver la liberte et la verite."
echo.
echo ===============================================
echo.
echo Serveur toujours actif sur http://localhost:3000
echo Pour arreter: taskkill //F //PID [PID du node.exe]
echo.
REM Clean up temp file
del "%TEMP_FILE%" >nul 2>&1
endlocal

View File

@ -426,29 +426,6 @@
}
]
},
"ils": {
"traductions": [
{
"confluent": "tasu",
"type": "pronom",
"categorie": "personnel",
"note": "Pronom 3ème personne pluriel"
}
],
"synonymes_fr": [
"elles"
]
},
"nous": {
"traductions": [
{
"confluent": "tanu",
"type": "pronom",
"categorie": "personnel",
"note": "Pronom 1ère personne pluriel"
}
]
},
"sa": {
"traductions": [
{
@ -533,6 +510,22 @@
"proche",
"alentour"
]
},
"tandis": {
"traductions": [
{
"confluent": "ta",
"type": "particule",
"categorie": "temporel",
"note": "Tandis que, pendant que - conjonction temporelle de simultanéité"
}
],
"synonymes_fr": [
"tandis que",
"pendant",
"pendant que",
"alors que"
]
}
}
}

View File

@ -618,6 +618,106 @@
"note": "Nouvelle racine - ce qui est caché"
}
]
},
"bas": {
"traductions": [
{
"confluent": "baso",
"type": "racine",
"forme_liee": "bas",
"domaine": "qualificatif",
"note": "Bas, inférieur (opposé de haut)"
}
],
"synonymes_fr": [
"basse",
"basses"
]
},
"long": {
"traductions": [
{
"confluent": "lono",
"type": "racine",
"forme_liee": "lon",
"domaine": "qualificatif",
"note": "Long, étendu (opposé de court)"
}
],
"synonymes_fr": [
"longue",
"longs",
"longues"
]
},
"chaud": {
"traductions": [
{
"confluent": "suka",
"type": "racine",
"forme_liee": "suk",
"domaine": "qualificatif",
"note": "Chaud, chaleureux (lié à feu)"
}
],
"synonymes_fr": [
"chaude",
"chauds",
"chaudes",
"chaleur"
]
},
"froid": {
"traductions": [
{
"confluent": "kiso",
"type": "racine",
"forme_liee": "kis",
"domaine": "qualificatif",
"note": "Froid, frais, glacé"
}
],
"synonymes_fr": [
"froide",
"froids",
"froides",
"frais",
"fraîche"
]
},
"mauvais": {
"traductions": [
{
"confluent": "daku",
"type": "racine",
"forme_liee": "dak",
"domaine": "qualificatif",
"note": "Mauvais, négatif (opposé de bon)"
}
],
"synonymes_fr": [
"mauvaise",
"mauvaises",
"mal"
]
},
"clair": {
"traductions": [
{
"confluent": "sora",
"type": "racine",
"forme_liee": "sor",
"domaine": "qualificatif",
"note": "Clair, lumineux (lié à lumière)"
}
],
"synonymes_fr": [
"claire",
"clairs",
"claires",
"lumineux",
"lumineuse"
]
}
},
"pronoms": {
@ -663,6 +763,45 @@
"elle",
"iel"
]
},
"nous": {
"traductions": [
{
"confluent": "mikisu",
"type": "pronom",
"composition": "miki-su",
"racines": ["miki", "su"],
"personne": "1pl",
"note": "Première personne pluriel - miki (je) + su (pluriel)"
}
]
},
"vous": {
"traductions": [
{
"confluent": "sinusu",
"type": "pronom",
"composition": "sinu-su",
"racines": ["sinu", "su"],
"personne": "2pl",
"note": "Deuxième personne pluriel - sinu (tu) + su (pluriel)"
}
]
},
"ils": {
"traductions": [
{
"confluent": "tanisu",
"type": "pronom",
"composition": "tani-su",
"racines": ["tani", "su"],
"personne": "3pl",
"note": "Troisième personne pluriel - tani (il/elle) + su (pluriel)"
}
],
"synonymes_fr": [
"elles"
]
}
}
}

View File

@ -240,7 +240,14 @@
"synonymes_fr": [
"parler",
"dites",
"dit"
"dit",
"murmurer",
"murmure",
"murmures",
"murmurons",
"murmurez",
"murmurent",
"chuchoter"
]
},
"savoir": {
@ -263,7 +270,12 @@
"saches",
"sachent",
"sachions",
"sachiez"
"sachiez",
"comprendre",
"comprends",
"comprenons",
"comprenez",
"comprennent"
]
},
"apprendre": {
@ -1052,6 +1064,123 @@
"envolez",
"envolent"
]
},
"aimer": {
"racine_fr": "aim",
"traductions": [
{
"confluent": "koris",
"type": "verbe",
"racine": "kori",
"forme_liee": "kor",
"structure": "CVCVC",
"domaine": "action_emotion",
"note": "Verbe d'amour (du cœur kori)"
}
],
"synonymes_fr": [
"aime",
"aimes",
"aimons",
"aimez",
"aiment",
"aimais",
"aimait",
"aimions",
"aimiez",
"aimaient",
"aimant",
"aimé"
]
},
"pouvoir": {
"racine_fr": "pouv",
"traductions": [
{
"confluent": "polas",
"type": "verbe",
"racine": "pola",
"forme_liee": "pol",
"structure": "CVCVC",
"domaine": "action_capacite",
"note": "Avoir la capacité de"
}
],
"synonymes_fr": [
"peux",
"peut",
"pouvons",
"pouvez",
"peuvent",
"pouvais",
"pouvait",
"pouvions",
"pouviez",
"pouvaient",
"pouvant",
"pu"
]
},
"devoir": {
"racine_fr": "dev",
"traductions": [
{
"confluent": "nekas",
"type": "verbe",
"racine": "neka",
"forme_liee": "nek",
"structure": "CVCVC",
"domaine": "action_obligation",
"note": "Être obligé de, avoir l'obligation (lié à œuvre/tâche)"
}
],
"synonymes_fr": [
"dois",
"doit",
"devons",
"devez",
"doivent",
"devais",
"devait",
"devions",
"deviez",
"devaient",
"devant",
"dû",
"due",
"dus",
"dues"
]
},
"reposer": {
"racine_fr": "repos",
"traductions": [
{
"confluent": "tokas",
"type": "verbe",
"racine": "toka",
"forme_liee": "tok",
"structure": "CVCVC",
"domaine": "action_fondement",
"note": "Reposer sur, être fondé sur (lié à toka=lieu/base)"
}
],
"synonymes_fr": [
"repose",
"reposes",
"reposons",
"reposez",
"reposent",
"reposais",
"reposait",
"reposions",
"reposiez",
"reposaient",
"reposant",
"reposé",
"être fondé",
"se fonder"
]
}
}
}

View File

@ -564,6 +564,108 @@
"domaine": "conflit"
}
]
},
"civilisation": {
"traductions": [
{
"confluent": "uraikota",
"type": "composition",
"composition": "ura-i-kota",
"sens_litteral": "Confluence-de-vie",
"racines": ["ura", "kota"],
"liaison": "i",
"domaine": "concept_fondateur",
"note": "Civilisation - l'union des gens qui vivent ensemble (ura=eau/vie + i=agentivité + kota=confluence/union)"
}
],
"synonymes_fr": [
"civilisations",
"société"
]
},
"observation": {
"traductions": [
{
"confluent": "silimira",
"type": "composition",
"composition": "sili-mira",
"sens_litteral": "Regard-observant",
"racines": ["sili", "mirak"],
"domaine": "concept_fondateur",
"note": "Observation - acte de regarder avec attention (sili=regard + mirak=observer)"
}
],
"synonymes_fr": [
"observations",
"contemplation"
]
},
"culture": {
"traductions": [
{
"confluent": "aitatoka",
"type": "composition",
"composition": "aita-toka",
"sens_litteral": "Lieux-des-ancêtres",
"racines": ["aita", "toka"],
"domaine": "concept_fondateur",
"note": "Culture - les lieux/traditions des ancêtres transmis (aita=ancêtres + toka=lieux)"
}
],
"synonymes_fr": [
"cultures",
"patrimoine"
]
},
"connaissance": {
"traductions": [
{
"confluent": "sekamemu",
"type": "composition",
"composition": "seka-memu",
"sens_litteral": "Savoir-en-mémoire",
"racines": ["seka", "memu"],
"domaine": "concept_fondateur",
"note": "Connaissance - le savoir gardé en mémoire (seka=savoir + memu=mémoire)"
}
],
"synonymes_fr": [
"connaissances",
"science",
"érudition"
]
},
"mémoire": {
"traductions": [
{
"confluent": "memu",
"type": "racine",
"forme_liee": "mem",
"domaine": "concept_fondateur",
"note": "Mémoire - concept central de la civilisation (synonyme normalisé de memoire)"
}
],
"synonymes_fr": [
"memoire",
"souvenir",
"souvenirs"
]
},
"liberté": {
"traductions": [
{
"confluent": "aska",
"type": "racine_sacree",
"forme_liee": "ask",
"domaine": "concept_fondateur",
"note": "Liberté - racine sacrée centrale (synonyme normalisé de liberte)"
}
],
"synonymes_fr": [
"liberte",
"libre",
"affranchissement"
]
}
}
}

View File

@ -19,16 +19,29 @@
"generation": {
"traductions": [
{
"confluent": "temiuaita",
"confluent": "noviuaita",
"type": "composition",
"composition": "tem-i-aita",
"sens_litteral": "Temps des ancêtres",
"composition": "nov-i-u-aita",
"sens_litteral": "Nouveaux (qui deviendront) ancêtres",
"racines": [
"temi",
"nuvi",
"aita"
],
"domaine": "temps"
"liaison": "i-u",
"structure": "composition",
"domaine": "temps",
"note": "Nouvelle génération - ceux qui porteront la mémoire (nov=nouveau + i=agentivité + u=appartenance + aita=ancêtres)"
}
],
"synonymes_fr": [
"génération",
"generations",
"générations",
"nouvelle generation",
"nouvelle génération",
"nouvelles generations",
"nouvelles générations",
"descendants"
]
},
"siecle": {

View File

@ -0,0 +1,463 @@
{
"_comment": "Vocabulaire alimentaire et culinaire de la Confluence",
"_source": "Basé sur civjdr/Background/2024-10-28-le-village.md",
"dictionnaire": {
"poisson": {
"racine_fr": "poiss",
"traductions": [
{
"confluent": "pisu",
"type": "racine",
"forme_liee": "pis",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Poisson - source vitale de protéines (même racine que pêcher)"
}
],
"synonymes_fr": [
"poissons"
]
},
"gibier": {
"racine_fr": "gibier",
"traductions": [
{
"confluent": "zana",
"type": "racine",
"forme_liee": "zan",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Gibier chassé (lié à zanak-chasser)"
}
],
"synonymes_fr": [
"proie"
]
},
"baie": {
"racine_fr": "bai",
"traductions": [
{
"confluent": "beka",
"type": "racine",
"forme_liee": "bek",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Baies - fruits sauvages essentiels"
}
],
"synonymes_fr": [
"baies",
"petits fruits"
]
},
"tubercule": {
"racine_fr": "tubercul",
"traductions": [
{
"confluent": "tuba",
"type": "racine",
"forme_liee": "tub",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Tubercules, racines comestibles"
}
],
"synonymes_fr": [
"racine",
"tubercules"
]
},
"fruit": {
"racine_fr": "fruit",
"traductions": [
{
"confluent": "veka",
"type": "racine",
"forme_liee": "vek",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Fruits génériques"
}
],
"synonymes_fr": [
"fruits"
]
},
"mollusque": {
"racine_fr": "mollusqu",
"traductions": [
{
"confluent": "molu",
"type": "racine",
"forme_liee": "mol",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Mollusques, coquillages"
}
],
"synonymes_fr": [
"coquillage",
"mollusques"
]
},
"graine": {
"racine_fr": "grain",
"traductions": [
{
"confluent": "seka",
"type": "racine",
"forme_liee": "sek",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Graines comestibles"
}
],
"synonymes_fr": [
"graines",
"semence"
]
},
"galette": {
"racine_fr": "galet",
"traductions": [
{
"confluent": "panu",
"type": "racine",
"forme_liee": "pan",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Galette, pain plat"
}
],
"synonymes_fr": [
"pain",
"crêpe",
"galettes"
]
},
"herbe": {
"racine_fr": "herb",
"traductions": [
{
"confluent": "pala",
"type": "racine",
"forme_liee": "pal",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Herbes aromatiques"
}
],
"synonymes_fr": [
"herbes"
]
},
"aromate": {
"racine_fr": "aromat",
"traductions": [
{
"confluent": "palusuki",
"type": "composition",
"composition": "pal-u-suki",
"sens_litteral": "Herbe du feu (qui brûle le goût)",
"racines": ["pala", "suki"],
"liaison": "u",
"structure": "composition",
"domaine": "nourriture",
"note": "Aromate, épice - herbe qui réchauffe/brûle (piquant)"
}
],
"synonymes_fr": [
"aromates",
"epice",
"epices",
"épice",
"épices"
]
},
"morsure-des-ancetres": {
"racine_fr": null,
"traductions": [
{
"confluent": "aiteopalu",
"type": "composition",
"composition": "ait-eo-palu",
"sens_litteral": "Ancêtre-éternel-qui-brûle",
"racines": ["aita", "palu"],
"liaison": "eo",
"structure": "composition_sacree",
"domaine": "nourriture_sacree",
"note": "Gingembre sauvage très estimé, épice sacrée - utilise liaison sacrée eo (totalité/éternel)"
}
],
"synonymes_fr": [
"gingembre sauvage",
"epice sacree"
]
},
"larmes-du-ciel": {
"racine_fr": null,
"traductions": [
{
"confluent": "zeruosi",
"type": "composition",
"composition": "zer-u-osi",
"sens_litteral": "Ciel-de-mort (larmes célestes)",
"racines": ["zeru", "osi"],
"liaison": "u",
"structure": "composition_sacree",
"domaine": "nourriture_sacree",
"note": "Plat cérémoniel traditionnel - poisson fumé, gibier, Morsure-des-Ancêtres, herbes et baies. Utilise liaison u (appartenance)"
}
],
"synonymes_fr": [
"plat ceremoniel",
"plat traditionnel"
]
},
"fumer": {
"racine_fr": "fum",
"traductions": [
{
"confluent": "simus",
"type": "verbe",
"racine": "simu",
"forme_liee": "sim",
"structure": "CVCV",
"domaine": "technique_culinaire",
"note": "Fumer (aliment), technique de conservation"
}
],
"synonymes_fr": [
"fume",
"fumes",
"fumons",
"fumez",
"fument",
"fumais",
"fumait",
"fumions",
"fumiez",
"fumaient",
"fumant",
"fumé",
"fumée"
]
},
"secher": {
"racine_fr": "séch",
"traductions": [
{
"confluent": "sekus",
"type": "verbe",
"racine": "seku",
"forme_liee": "sek",
"structure": "CVCV",
"domaine": "technique_culinaire",
"note": "Sécher (aliment), conservation"
}
],
"synonymes_fr": [
"sécher",
"sèche",
"sèches",
"séchons",
"séchez",
"sèchent",
"séchais",
"séchait",
"séchions",
"séchiez",
"séchaient",
"séchant",
"séché",
"sec",
"sèche"
]
},
"griller": {
"racine_fr": "grill",
"traductions": [
{
"confluent": "palus",
"type": "verbe",
"racine": "palu",
"forme_liee": "pal",
"structure": "CVCV",
"domaine": "technique_culinaire",
"note": "Griller, cuire sur feu"
}
],
"synonymes_fr": [
"grille",
"grilles",
"grillons",
"grillez",
"grillent",
"grillais",
"grillait",
"grillions",
"grilliez",
"grillaient",
"grillant",
"grillé",
"cuire",
"rôtir"
]
},
"cuisiner": {
"racine_fr": "cuisin",
"traductions": [
{
"confluent": "mukunekas",
"type": "composition",
"composition": "muk-u-nekas",
"sens_litteral": "Nourriture-de-faire",
"racines": ["muki", "nekas"],
"liaison": "u",
"structure": "composition",
"domaine": "technique_culinaire",
"note": "Cuisiner, préparer (faire de la nourriture) - muki + liaison u + nekas"
}
],
"synonymes_fr": [
"cuisine",
"cuisines",
"cuisinons",
"cuisinez",
"cuisinent",
"cuisinais",
"cuisinait",
"cuisinions",
"cuisiniez",
"cuisinaient",
"cuisinant",
"cuisiné",
"preparer la nourriture",
"préparer la nourriture"
]
},
"infuser": {
"racine_fr": "infus",
"traductions": [
{
"confluent": "urapis",
"type": "composition",
"composition": "ura-pis",
"sens_litteral": "Eau-tremper",
"racines": ["ura", "pisu"],
"structure": "composition",
"domaine": "technique_culinaire",
"note": "Infuser, faire tremper dans l'eau"
}
],
"synonymes_fr": [
"infuse",
"infusion",
"tremper"
]
},
"reserve": {
"racine_fr": "réserv",
"traductions": [
{
"confluent": "zaku",
"type": "racine",
"forme_liee": "zak",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Réserves alimentaires (lié à garder/protéger)"
}
],
"synonymes_fr": [
"réserves",
"provisions",
"stock"
]
},
"nourriture": {
"racine_fr": "nourritur",
"traductions": [
{
"confluent": "muki",
"type": "racine",
"forme_liee": "muk",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Nourriture (racine de manger)"
}
],
"synonymes_fr": [
"aliment",
"aliments",
"repas"
]
},
"boire": {
"racine_fr": "boir",
"traductions": [
{
"confluent": "urapis",
"type": "verbe",
"racine": "ura",
"forme_liee": "ura",
"structure": "VCVCV",
"domaine": "nourriture",
"note": "Boire - utilise racine sacrée ura (eau)"
}
],
"synonymes_fr": [
"bois",
"boit",
"buvons",
"buvez",
"boivent",
"buvais",
"buvait",
"buvions",
"buviez",
"buvaient",
"buvant",
"bu"
]
},
"legume": {
"racine_fr": "légum",
"traductions": [
{
"confluent": "leva",
"type": "racine",
"forme_liee": "lev",
"structure": "CVCV",
"domaine": "nourriture",
"note": "Légume, plante comestible cultivée"
}
],
"synonymes_fr": [
"légume",
"légumes",
"plante comestible"
]
},
"manque": {
"racine_fr": "manqu",
"traductions": [
{
"confluent": "zomuki",
"type": "composition",
"composition": "zo-muki",
"sens_litteral": "Négation-nourriture",
"racines": ["zo", "muki"],
"structure": "composition",
"domaine": "nourriture",
"note": "Manque (de nourriture) - zo (négation) + muki (nourriture)"
}
],
"synonymes_fr": [
"manquer",
"absence",
"pénurie"
]
}
}
}

View File

@ -0,0 +1,164 @@
{
"_comment": "Vocabulaire de l'habitat et de l'architecture",
"_source": "Extension vocabulaire quotidien",
"dictionnaire": {
"table": {
"racine_fr": "tabl",
"traductions": [
{
"confluent": "mukisloku",
"type": "composition",
"composition": "mukis-loku",
"sens_litteral": "Lieu-de-manger",
"racines": ["mukis", "loku"],
"structure": "composition",
"domaine": "habitat",
"note": "Table - lieu où l'on mange (mukis=manger + loku=lieu)"
}
],
"synonymes_fr": [
"tables",
"surface pour manger"
]
},
"fenetre": {
"racine_fr": "fenêtr",
"traductions": [
{
"confluent": "soratuva",
"type": "composition",
"composition": "sora-tuva",
"sens_litteral": "Ouverture-de-lumière",
"racines": ["sora", "tuva"],
"structure": "composition",
"domaine": "habitat",
"note": "Fenêtre - ouverture qui laisse passer la lumière (sora=lumière + tuva=ouverture)"
}
],
"synonymes_fr": [
"fenêtre",
"fenêtres",
"ouverture"
]
},
"toit": {
"racine_fr": "toit",
"traductions": [
{
"confluent": "zakusumu",
"type": "composition",
"composition": "zaku-sumu",
"sens_litteral": "Protection-haute",
"racines": ["zaku", "sumu"],
"structure": "composition",
"domaine": "habitat",
"note": "Toit - protection en hauteur (zaku=protection/réserve + sumu=haut)"
}
],
"synonymes_fr": [
"toits",
"toiture",
"abri"
]
},
"mur": {
"racine_fr": "mur",
"traductions": [
{
"confluent": "karisumu",
"type": "composition",
"composition": "kari-sumu",
"sens_litteral": "Pierre-dressée",
"racines": ["kari", "sumu"],
"structure": "composition",
"domaine": "habitat",
"note": "Mur - pierre élevée (kari=pierre + sumu=haut/élevé)"
}
],
"synonymes_fr": [
"murs",
"paroi",
"cloison"
]
},
"escalier": {
"racine_fr": "escalier",
"traductions": [
{
"confluent": "vukukari",
"type": "composition",
"composition": "vuku-kari",
"sens_litteral": "Gouffre-de-pierre",
"racines": ["vuku", "kari"],
"structure": "composition",
"domaine": "habitat",
"note": "Escalier - montée de pierre (référence au Gouffre Humide avec ses escaliers dans la roche, vuku=gouffre + kari=pierre)"
}
],
"synonymes_fr": [
"escaliers",
"marches",
"montée"
]
},
"ouvrir": {
"racine_fr": "ouvr",
"traductions": [
{
"confluent": "tuvas",
"type": "verbe",
"racine": "tuva",
"forme_liee": "tuv",
"structure": "CVCV",
"domaine": "action",
"note": "Ouvrir, créer une ouverture"
}
],
"synonymes_fr": [
"ouvre",
"ouvres",
"ouvrons",
"ouvrez",
"ouvrent",
"ouvrais",
"ouvrait",
"ouvrions",
"ouvriez",
"ouvraient",
"ouvrant",
"ouvert",
"ouverture"
]
},
"monter": {
"racine_fr": "mont",
"traductions": [
{
"confluent": "sumus",
"type": "verbe",
"racine": "sumu",
"forme_liee": "sum",
"structure": "CVCV",
"domaine": "action",
"note": "Monter, s'élever (même racine que haut)"
}
],
"synonymes_fr": [
"monte",
"montes",
"montons",
"montez",
"montent",
"montais",
"montait",
"montions",
"montiez",
"montaient",
"montant",
"monté",
"grimper",
"élever"
]
}
}
}