File size: 18,018 Bytes
9946f74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a698c9a
d866935
ce864ca
9946f74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1aaad17
9946f74
 
a9cccf6
57ab0ee
b99e3ea
9946f74
 
2ea0b83
9946f74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1aaad17
9946f74
 
ce864ca
3058825
d866935
3058825
2a01be5
54e74a9
02a81a6
9946f74
 
 
aeaa0a2
855a064
1aaad17
e102072
54e74a9
9946f74
 
 
2ea0b83
9946f74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e17e0fb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ce0273
e17e0fb
 
8ce0273
e17e0fb
 
 
 
8ce0273
e17e0fb
8ce0273
e17e0fb
8ce0273
e17e0fb
8ce0273
e17e0fb
 
 
 
 
 
 
 
47a1725
e17e0fb
 
 
 
 
 
 
 
 
 
 
 
 
 
70bb40b
e17e0fb
 
 
 
47a1725
e4460cf
e17e0fb
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
import re
import gradio as gr
import unicodedata

# -----------------------------
# UTILITAIRES DE NORMALISATION
# -----------------------------
def normalize(text):
    text = text.lower()
    text = unicodedata.normalize('NFD', text)
    return ''.join(c for c in text if unicodedata.category(c) != 'Mn')

def build_norm_map(original):
    norm_chars = []
    mapping = []
    for i, ch in enumerate(original):
        decomposed = unicodedata.normalize('NFD', ch)
        base_chars = ''.join(c for c in decomposed if unicodedata.category(c) != 'Mn')
        if base_chars == '':
            continue
        for nb in base_chars:
            norm_chars.append(nb.lower())
            mapping.append(i)
    return ''.join(norm_chars), mapping

def slice_original_from_norm_span(orig, map_norm, start_norm, end_norm):
    start_orig = map_norm[start_norm]
    end_orig = map_norm[end_norm - 1] + 1
    return orig[start_orig:end_orig]

# -----------------------------
# LISTES EXHAUSTIVES
# -----------------------------
intensificateurs = [
    "trop", "très", "fort", "forte", "forts", "fortes",
    "bien", "furieusement", "intensément", "tout", "tant",
    "complètement", "pleinement", "violemment", "parfaitement",
    "totalement", "beaucoup", "énormément", "extrêmement",
    "hyper", "profondément", "follement", "absolument", "vraiment "
    "extraordinairement", "incroyablement", "terriblement","particulièrement ","singulièrement "
    "prodigieusement", "excessivement", "vachement", "bigrement","puissamment ","fortement ","hautement ", " considérablement ","remarquablement "
]

moderateurs = [
    "assez", "modérément", "moyennement", "quasi",
    "quasiment", "presque", "pas mal", "plutôt",
    "relativement", "légèrement", "quelque peu",
    "un tantinet"
]

attenuateurs = [
    "peu", "à peine", "médiocrement", "passablement",
    "petitement", "un peu", "faiblement", "timidement",
    "un brin", "modiquement"
]

adj_intensificateurs = [
    "énorme", "énormes", "immense", "immenses", "extrême", "extrêmes",
    "suprême", "suprêmes", "immensurable", "immensurables",
    "profond", "profonde", "profondes", "profonds",
    "parfait", "parfaite", "parfaits", "parfaites",
    "irréprochable", "irréprochables",
    "implacable", "implacables",
    "insondable", "insondables",
    "infini", "infinie", "infinis", "infinies",
    "formidable", "formidables",
    "exceptionnel", "exceptionnelle", "exceptionnels", "exceptionnelles",
    "incroyable", "incroyables",
    "extraordinaire", "extraordinaires",
    "fantastique", "fantastiques",
    "gigantesque", "gigantesques",
    "monumental", "monumentale", "monumentales", "monumentaux",
    "colossal", "colossale", "colossaux", "colossales", "remarquable ","remarquables"
    "phénoménal", "phénoménale", "phénoménaux", "phénoménales",
    "immenses", "intense", "intenses",
    "absolu", "absolue", "absolus", "absolues","titanesque ","titanesques ","herculéenne ","herculéen","herculéens", "herculéennes"
    "appuyé", "appuyés", "appuyée","maximal","maximale","maximaux","maximales","optimal","optimale","optimaux","optimales","appuyées","démesuré ","démesurée ","démesurés ","démesurées ",
    "furieux ","furieuse ","furieuses ","frappant ","frappante ","fulgurante ","fulgurant","fulgurants","fulgurantes ","considérable ","considérables ","élevé ","élevée ","élevés ","élevées "
]

adj_attenuateurs = ["infime","infimes","minime","minimes","minuscules","minuscule","minimale","minimal","minimales","minimaux"]

prefixes_intensifs = ["archi", "extra", "super", "hyper", "sur", "mega"]
prefixes_attenuateurs = ["sous", "hypo", "infra", "mini"]

# -----------------------------
# PHRASÉOLOGISMES
# -----------------------------
phras_intensifs = [
    "avoir le cafard", "ai le cafard", "as le cafard", "a le cafard",
    "avons le cafard", "avez le cafard", "ont le cafard",
    "avais le cafard", "avait le cafard", "avions le cafard",
    "aviez le cafard", "avaient le cafard",
    "aurai le cafard", "aura le cafard", "aurons le cafard",
    "aurez le cafard", "auront le cafard",
    "aurais le cafard", "aurait le cafard", "aurions le cafard",
    "auriez le cafard", "auraient le cafard",
    "être au septième ciel", "suis au septième ciel", "es au septième ciel",
    "est au septième ciel", "sommes au septième ciel", "êtes au septième ciel",
    "sont au septième ciel", "étais au septième ciel", "était au septième ciel",
    "étions au septième ciel", "étiez au septième ciel", "étaient au septième ciel",
    "serai au septième ciel", "sera au septième ciel", "serons au septième ciel",
    "serez au septième ciel", "seront au septième ciel",
    "serais au septième ciel", "serait au septième ciel", "serions au septième ciel",
    "seriez au septième ciel", "seraient au septième ciel",
    "se tordre de douleur", "me tords de douleur", "te tords de douleur",
    "se tord de douleur", "nous nous tordons de douleur", "vous vous tordez de douleur",
    "se tordent de douleur",
    "mourir de rire", "meurs de rire", "meurt de rire", "mourons de rire",
    "mourez de rire", "meurent de rire",
    "mourir de faim", "meurs de faim", "meurt de faim", "mourons de faim",
    "mourez de faim", "meurent de faim",
    "mourir de froid", "meurs de froid", "meurt de froid", "mourons de froid","de plus en plus "
    "mourez de froid", "meurent de froid",
    "brûler d'amour", "brûle d'amour", "brûlait d'amour", "brûlerai d'amour",
    "brûler d'impatience", "brûle d'impatience", "brûlait d'impatience", "brûlerai d'impatience","au plus vite ", "au plus haut degré ","à l'extrême "
    "trembler de peur", "tremble de peur","fort comme un turc","rouge comme une tomate","malade comme un chien", "tremblait de peur", "tremblerai de peur",
    "être transporté de joie", "suis transporté de joie", "est transporté de joie", "sommes transportés de joie", "à verse ",
    "à couper le souffle","à mort","à fond","à pas de géant","plein à craquer","laid à pleurer","hors de lui","au comble de la joie", "au comble de la performance",
    "fièvre de cheval ","froideur de glace ","faim de loup ", "froid de canard","appétit d'orge","patience d'ange ", " comme une souche ","comme un trou ","comme un putois",
    "avoir le cœur en capilotade ", "à bout de force ", "à bout de souffle ", "se traîner comme une limace ","timide comme une souris ", "froid comme un glaçon ", " d'une lenteur d'escargot ",
    "faible comme une plume ","avoir les jambes en coton ","mal en point ","pluie diluvienne ","force herculéenne ","froid sibérien ","à gorge déployée ","à toute allure ","à toute vitesse ", "de toutes forces"
]

phras_att = [
    "appétit d'oiseau","force de moineau",
    
     "de faible envergure"," de moins en moins "
    
    
]

phras_mod = [
    "un peu comme", "dans une certaine mesure", "à moitié chemin", "à la limite", "mettre la main à la pâte"
]

# -----------------------------
# COMPARATIFS & SUPERLATIFS
# -----------------------------
comp_super_intens = ["plus que", "plus de", "meilleur", "meilleure", "meilleurs", "meilleures", "mieux",
                     "le plus", "la plus", "les plus", "le meilleur", "la meilleure", "les meilleurs", "les meilleures",
                     "le mieux"]
comp_super_att = ["moins que", "moins de", "moindre", "moindres", "le moins", "la moindre", "les moindres"]
comp_super_mod = ["aussi que", "autant que", "autant de"]

# -----------------------------
# NORMALISATION
# -----------------------------
def build_norm(items):
    return [normalize(x) for x in items]

norm_intens = build_norm(intensificateurs + adj_intensificateurs + comp_super_intens)
norm_mod = build_norm(moderateurs + comp_super_mod)
norm_att = build_norm(attenuateurs + adj_attenuateurs + comp_super_att)
norm_phras_int = build_norm(phras_intensifs)
norm_phras_att = build_norm(phras_att)
norm_phras_mod = build_norm(phras_mod)
norm_adj_int = build_norm(adj_intensificateurs)
norm_prefixes_int = [p.lower() for p in prefixes_intensifs]
# -----------------------------
# FONCTIONS DE DÉTECTION (suite)
# -----------------------------
def detect_multiword_with_gaps(text_norm, map_norm, orig, phrase_norm, max_gaps=3):
    matches = []
    parts = phrase_norm.split()
    if not parts:
        return matches
    escaped = [re.escape(p) for p in parts]
    gap = r'(?:\s+\w+){0,' + str(max_gaps) + r'}\s*'
    pattern = r'\b' + gap.join(escaped) + r'\b'
    for m in re.finditer(pattern, text_norm):
        s, e = m.start(), m.end()
        matches.append((s, e, slice_original_from_norm_span(orig, map_norm, s, e)))
    return matches

def detect_words_general(text_norm, map_norm, orig, norm_items, occupied):
    matches = []
    for item in sorted(set(norm_items), key=lambda s: -len(s)):
        if not item: continue
        for s,e,orig_sub in detect_multiword_with_gaps(text_norm, map_norm, orig, item, max_gaps=3):
            if any(occupied[s:e]): continue
            for i in range(s,e): occupied[i]=True
            matches.append((s,e,orig_sub))
    return matches

def detect_un_peu_and_peu(text_norm, map_norm, orig, occupied):
    matches = []
    for m in re.finditer(r'\bun\s+peu\b', text_norm):
        s,e = m.start(), m.end()
        if any(occupied[s:e]): continue
        for i in range(s,e): occupied[i]=True
        matches.append((s,e,slice_original_from_norm_span(orig,map_norm,s,e)))
    for m in re.finditer(r'\bpeu\b', text_norm):
        s,e = m.start(), m.end()
        if any(occupied[s:e]): continue
        for i in range(s,e): occupied[i]=True
        matches.append((s,e,slice_original_from_norm_span(orig,map_norm,s,e)))
    return matches

def detect_bien_plus_adj(text_norm, map_norm, orig, occupied, adj_norm_list):
    matches = []
    for m in re.finditer(r'\bbien\b', text_norm):
        if re.match(r'\bbien\s+que\b', text_norm[m.start():]): continue
        for adj in adj_norm_list:
            pat = r'\bbien(?:\s+\w+){0,2}\s+' + re.escape(adj) + r'\b'
            for mm in re.finditer(pat, text_norm):
                s,e = mm.start(), mm.end()
                if any(occupied[s:e]): continue
                for i in range(s,e): occupied[i]=True
                matches.append((s,e,slice_original_from_norm_span(orig,map_norm,s,e)))
    return matches

def detect_prefixed_adjectives(text_norm, map_norm, orig, occupied, prefixes_norm, adj_norm_list):
    matches = []
    for m in re.finditer(r'\b\w+\b', text_norm):
        s,e = m.start(), m.end()
        if any(occupied[s:e]): continue
        token = text_norm[s:e]
        for p in prefixes_norm:
            if token.startswith(p) and len(token) > len(p)+1:
                for i in range(s,e): occupied[i]=True
                matches.append((s,e,slice_original_from_norm_span(orig,map_norm,s,e)))
                break
    return matches

def detect_comp_plus_minus_aussi_with_adj(text_norm, map_norm, orig, occupied):
    res = {'plus':[], 'moins':[], 'aussi':[]}
    for key in ['plus','moins','aussi']:
        pat = r'\b' + key + r'(?:\s+\w+){0,3}\s+\w+\b'
        for m in re.finditer(pat, text_norm):
            s,e = m.start(), m.end()
            if any(occupied[s:e]): continue
            for i in range(s,e): occupied[i]=True
            res[key].append((s,e,slice_original_from_norm_span(orig,map_norm,s,e)))
    return res

# -----------------------------
# ANALYSEUR PRINCIPAL
# -----------------------------
def analyse_text(text):
    if not text or not text.strip(): return "Entrez un texte valide."
    orig = text
    text_norm, map_norm = build_norm_map(orig)
    occupied = [False] * max(1,len(text_norm))

    # phraséologismes  
    phras_int = detect_words_general(text_norm,map_norm,orig,norm_phras_int,occupied)  
    phras_mod = detect_words_general(text_norm,map_norm,orig,norm_phras_mod,occupied)  
    phras_att = detect_words_general(text_norm,map_norm,orig,norm_phras_att,occupied)  

    # 'un peu' et 'peu'  
    un_peu_matches = detect_un_peu_and_peu(text_norm,map_norm,orig,occupied)  

    # 'bien + adj'  
    bien_adj_matches = detect_bien_plus_adj(text_norm,map_norm,orig,occupied,norm_adj_int)  

    # préfixes  
    prefixed_matches = detect_prefixed_adjectives(text_norm,map_norm,orig,occupied,norm_prefixes_int,norm_adj_int)  

    # comparatifs + adj  
    comp_matches = detect_comp_plus_minus_aussi_with_adj(text_norm,map_norm,orig,occupied)  

    # général lexical  
    general_intens = detect_words_general(text_norm,map_norm,orig,norm_intens,occupied)  
    general_mod = detect_words_general(text_norm,map_norm,orig,norm_mod,occupied)  
    general_att = detect_words_general(text_norm,map_norm,orig,norm_att,occupied)  

    intens_detect = general_intens + bien_adj_matches + prefixed_matches + comp_matches.get('plus',[])  
    moder_detect = general_mod + comp_matches.get('aussi',[])  
    atten_detect = general_att + un_peu_matches + comp_matches.get('moins',[])  

    # highlights  
    highlights = []  
    for s,e,txt in intens_detect: highlights.append((s,e,'intensifs',txt))  
    for s,e,txt in moder_detect: highlights.append((s,e,'moder',txt))  
    for s,e,txt in atten_detect: highlights.append((s,e,'atten',txt))  
    for s,e,txt in phras_int: highlights.append((s,e,'phras_int',txt))  
    for s,e,txt in phras_mod: highlights.append((s,e,'phras_mod',txt))  
    for s,e,txt in phras_att: highlights.append((s,e,'phras_att',txt))  
    highlights.sort(key=lambda x:x[0])  

    # HTML highlighting  
    html_out=""  
    last=0  
    colors = {  
        "intensifs":"background-color:#ff4d4d;",  
        "moder":"background-color:#a6e1ff;",  
        "atten":"background-color:#d9d9d9;",  
        "phras_int":"background-color:#ffb3b3;",  
        "phras_mod":"background-color:#cfe9ff;",  
        "phras_att":"background-color:#f0f0f0;"  
    }  
    def esc(s): return s.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;")  
    for s_norm,e_norm,cat,orig_sub in highlights:  
        try:  
            s_o = map_norm[s_norm]  
            e_o = map_norm[e_norm-1]+1  
            html_out += esc(orig[last:s_o])  
            html_out += f"<span style='{colors[cat]} padding:2px 4px; border-radius:4px'>{esc(orig[s_o:e_o])}</span>"  
            last=e_o  
        except:  
            html_out += esc(orig[last:])  
            last=len(orig)  
            break  
    html_out += esc(orig[last:])  

    # listes uniques  
    def uniq(seq):  
        seen=set()  
        out=[]  
        for s,e,txt in seq:  
            if txt not in seen:  
                seen.add(txt)  
                out.append(txt)  
        return out  
    intens_list=uniq(intens_detect)  
    moder_list=uniq(moder_detect)  
    atten_list=uniq(atten_detect)  
    phras_int_list=[t[2] for t in phras_int]  
    phras_mod_list=[t[2] for t in phras_mod]  
    phras_att_list=[t[2] for t in phras_att]  

    # note traduction  
    translate_notes=[]  
    for idi in phras_int_list:  
        translate_notes.append(f"- « {idi} » (intensificateur) — traduire avec précaution pour préserver la force expressive.")  
    for idi in phras_mod_list:  
        translate_notes.append(f"- « {idi} » (modérateur) — vérifier la nuance; une traduction littérale peut altérer le ton.")  
    for idi in phras_att_list:  
        translate_notes.append(f"- « {idi} » (atténuateur) — adapter selon le registre cible pour conserver l'effet atténuateur.")

    # Calcul du degré de modalisation  
    total_occurrences = len(intens_list) + len(moder_list) + len(atten_list) + len(phras_int_list) + len(phras_mod_list) + len(phras_att_list)  
    if total_occurrences <= 3:  
        deg_modal = "faible"  
    elif total_occurrences <= 6:  
        deg_modal = "moyen"  
    else:  
        deg_modal = "fort"  

    # Rapport final Markdown  
    report_md = f"""

🎯 RAPPORT D'ANALYSE — MODALISATION

Aperçu (occurrences surlignées) :

<div style="padding:10px;border:1px solid #ddd;border-radius:6px">{html_out}</div>  
---

💥 Intensificateurs détectés ({len(intens_list)}):
{', '.join(intens_list) if intens_list else 'Aucun'}

⚖️ Modérateurs détectés ({len(moder_list)}):
{', '.join(moder_list) if moder_list else 'Aucun'}

🌫️ Atténuateurs détectés ({len(atten_list)}):
{', '.join(atten_list) if atten_list else 'Aucun'}

---

🔎 Phraséologismes (séparés par sens)

🔥Intensificateurs ({len(phras_int_list)}): {', '.join(phras_int_list) if phras_int_list else 'Aucun'}

🌱Modérateurs ({len(phras_mod_list)}): {', '.join(phras_mod_list) if phras_mod_list else 'Aucun'}

🍃Atténuateurs ({len(phras_att_list)}): {', '.join(phras_att_list) if phras_att_list else 'Aucun'}

---

📝 Notes et conseils de traduction
{chr(10).join(translate_notes) if translate_notes else 'Aucun phraséologisme détecté.'}

---

📊 Degré de la modalisation intensive du texte : {deg_modal} (total occurrences : {total_occurrences})

---

🛠️ Note technique
Détection agile : comparatifs intégrés, préfixes traités (ex. hyper-), et distinctions 'bien + adj' / 'bien que' et 'un peu' / 'peu' sont gérées.
"""

    return report_md

# -----------------------------
# INTERFACE GRADIO
# -----------------------------
iface = gr.Interface(
    fn=analyse_text,
    inputs=gr.Textbox(label="Entrez le texte à analyser", lines=15,
                      placeholder="Collez votre texte ici..."),
    outputs=gr.Markdown(label="Rapport"),
    title="Analyseur de modalisateurs et d'intensité textuelle",
    description=("une application qui détecte et surligne les modalisateurs (intensificateurs, modérateurs et atténuateurs) "
                 "ainsi que les phraséologismes. Elle calcule ensuite le degré de la modalisation intensive du texte saisi. "
                 "Elle fournit finalement des notes et des conseils pour assurer l'équivalence traductive.")
)

if __name__ == "__main__":
    iface.launch()