liaisons.py 14.7 KB
Newer Older
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
from . import textPhonographer
import spacy

derniere_lettre = ['s', 'x', 'z', 't', 'd', 'n', 'p', 'f', 'r', 'g']


# Cette fonction va rechercher dans un texte brut s'il y a une locution.
# Si oui, on examine chaque token du texte pour savoir à quel indice se trouve cette
# locution et à quel indice la liaison se trouve
# entrée : texte brut + texte spacy
# sortie : index de la locution + index de la liaison (ou None) + type de la liaison ('O' ou 'F')
def liaison_locution(text, nlpText):

    # liste des locutions avec exceptions (à compléter)
    # clé : la locution
    # valeur : le mot après lequel se fait la liaison (0 est le premier mot, 1 le deuxième, etc)
    locutions = {"comment allez vous" : (0, 'O'),
                  "quand est ce" : (0, 'O'),
                  "de mieux en mieux" : (1, 'O'),
                  "petit à petit" : (0, 'O'),
                  "plus ou moins" : (0, 'O'),
                  "bien au contraire" : (0, 'O'),
                  "de plus en plus" : (1, 'O'),
                  "de temps en temps" : (1, 'O'),
                  "mot à mot" : (0, 'O'),
                  "pas à pas" : (0, 'O'),
                  "plus avant" : (0, 'O'),
                  "qui plus est" : (1, 'O'),
                  "pret à porter" : (0, 'O'),
                  "sous entendu" : (0, 'O'),
                  "pot au feu" : (0, 'O'),
                  "avant hier" : (0, 'O'),
                  "pied à terre" : (0, 'O'),
                  "non avenu" : (0, 'O'),
                  "nuit et jour" : (0, 'O'),
                  "vis à vis" : (0, 'O'),
                  "états unis" : (0, 'O'),
                  "de but en blanc" : (1, 'O'),
                  "de fond en comble" : (1, 'O'),
                  "moyen âge" : (0, 'O'),
                  "moyen Orient" : (0, 'O'),
                  "les beaux arts" : (1, 'O'),
                  "arts et métiers" : (0, 'O'),
                  "c'est à dire" : (1, 'O'),
                  "tout à fait" : (0, 'O'),
                  "tout à coup" : (0, 'O'),
                  "en effet" : (0, 'O'),
                  "à tout hasard" : (1, 'O'),
                  "peut être" : (0, 'O'),
                  "au fait et au prendre" : (1, 'O'),
                  "bien avant" : (0, 'O'),
                  "but à but" : (0, 'O'),
                  "vous et moi" : (0, 'O'),
                  "corps et âme" : (0, 'O'),
                  "corps et bien" : (0, 'O'),
                  "de bas en haut" : (1, 'O'),
                  "de fond en comble" : (1, 'O'),
                  "de haut en bas" : (1, 'O'),
                  "de temps à autre" : (1, 'O'),
                  "de moins en moins" : (1, 'O'),
                  "dès à présent" : (0, 'O'),
                  "du tout au tout" : (1, 'O'),
                  "pieds et poings liés" : (0, 'O'),
                  "rien à" : (0, 'O'),
                  "quand et quand" : (0, 'O'),
                  "de but en blanc" : (1, 'O'),
                  "bien être" : (0, 'O'),
                  "faits et gestes" : (1, 'O'),
                  ####FAC
                  "d'ores et déjà" : (1, 'F'),
                  "pas à pas" : (0, 'F'),
                  "dos à dos" : (0, 'F'),
                  "avec armes et bagages" : (1, 'F'),
                  "de part et d'autre" : (1, 'F'),
                  "d'un bout à l'autre" : (2, 'F'),
                  "par monts et par vaux" : (1, 'F')}
                  
    locution = None

    ## vérifier si une locution est présente
    # voir la dependance des mots d'apres
    for cle in locutions.keys():
        if cle in text.lower():
            locution = cle
            testloc = ""

    if locution != None :

        for j, token in enumerate(nlpText):
            locution2 = locution.replace(' ', '')   # chaîne sans espace pour éviter les problèmes de tokenization

            # vérifier si le premier mot correspond au début d'une locution
            if token.text.lower() == locution[:len(token.text)] and testloc=="":
                testloc+=token.text.lower()     # si oui on l'ajoute dans testloc (sans espace)
                index_locution = j  # enregistre où commence la locution

            elif token.text.lower() in locution and testloc!="":
                testloc+=token.text.lower()

                # si la variable de test correspond à la locution cherchée on retourne les indices
                if testloc == locution2:
                    index_liaison = locutions[locution][0]
                    liaison = locutions[locution][1]

                    # cas de "comment allez-vous" suivi d'une prep ou d'un verbe : il n'y a pas de liaison
                    if len(nlpText) > 3 and locution == "comment allez vous" and nlpText[j+1].pos_ in ['VERB', 'ADP'] :
                        return None

                    return index_locution, index_liaison, liaison
            else:
                testloc=""
    else:
        return None


#Fonction pour tester la liaison d'une suite de 2 mots
#en entree : 2 mots
#en sortie : un tuple de 2 mots, sinon NONE
def obtenirM1M2(mot1, mot2):

    lettre_voy = ['a','à','â','é','è','ê','ë','e','i','î','ï','o','ô','u','û','ù','ü','y','ÿ','œ']
    word2trans = textPhonographer.word2transFr # dic

    if mot1[-1] in lettre_voy :
        return None
    elif mot1[-1] in derniere_lettre :
        # voir si la première lettre de mot2 est une lettre voyelle
        if mot2[0].lower() in lettre_voy:
            # avec les mots en 'y' on ne fait souvent pas la liaison ('un yaourt', 'un yacht', etc)
            if mot2[0].lower() == 'y':
                if mot2 in ['yeux','y']:    # à compléter ?
                    return (mot1, mot2)
                else:
                    return None
            # print("Le mot2 qui commence par une lettre voyelle : "+mot2)
            return (mot1, mot2)
        # voir si la première lettre de mot2 est "h"
138
139
140
        elif mot2[0].lower() == 'h' and mot2.lower() in word2trans and "*" not in [y['tt'][0] for y in word2trans[mot2.lower()]['t']]:
            # si le mot commence par h mais n'a pas de *h dans toutes ses prononciations.
            print("Le mot2 commence par 'h' muet : " + mot2)
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
            return (mot1, mot2)
        else:
            # print("Le mot2 qui n'a pas de liaison : " + mot2)
            return None


#Fonction pour les exceptions
#en entree : 2 tokens
#en sortie : booleen
def exception(token1, token2):
    mots = ["oui", "ouf"]
    lettre = ['H','O','E','I','U','É', 'È','Œ','Â','Ê','Î','Ô','Û','Ä','Ë','Ï','Ö','Ü','Æ','Ù','â','é','è','ê','ë','e','i','î','ï','o','ô','u','û','ù','ü','ÿ','œ','æ']
    if token2.text in lettre or token2.text.lower() in mots:
        print ("Cas1 except : pas de liaison")
        return False
    elif token2.pos_ == "PROPN":
        print ("Cas2 except : pas de liaison")
        return False
    elif token1.text.lower() == "quand" and token2.text.lower() == "est":
        print ("Cas3 except : pas de liaison")
        return False
    elif token1.text.lower() == "comment" and token2.text.lower() == "allez":
        print ("Cas4 except : pas de liaison")
        return False
    else:
        return True


#Fonction pour les mots qui se terminent en g / f / r
#en entree : 1 mot
#en sortie : booleen
def gfr(mot1):
    lettre_gfr = ['g', 'f', 'r']

    if mot1.text[-1:] in lettre_gfr:
        if mot1.text[-2:] == "er":
            print("cas er")
            return True
        elif mot1.text.lower() == "neuf":
            print("cas neuf")
            return True
        elif mot1.text.lower() == "long":
            print("cas long")
            return True
        else:
            return False
            print("fini par gfr, mais pas le cas")
    else:
        return True


# C'est possible que le token1 ou le token2 est num, mais il n'est pas sûr que le pos de "num" sera identifié. donc on utilise la liate
# Par exemple, "un" est toujours "DET"

# Fonction pour identifier la liaison avec des nombres
# en entrée : 2 tokens
# en sortie : "O" (obligatoire), "F" (facultative), "N" (neuf) ou NONE
def liaison_nombre(token1,token2):

    mot_excep = ['ans','heures','hommes','autres']
    mois = ['avril', 'août', 'octobre']

    # Quand le nombre se trouve devant le mois, on ne fait pas de liaison
    # Exemple : le trois avril
    if token2.text.lower() in mois:
        print("Pas de liaison car 'mois'", token1.text, 'et', token2.text)
        return None

    # Devant "onze" ou "onzième", pas de liaison
    elif token2.text.lower() == "onze" or token2.text.lower() == "onzième" or token2.text.lower() == "onzièmes":
        print("Pas de liaison car 'onze'", token1.text, 'et', token2.text)
        return None

    # Après "vingt" ou "cent"
    elif token1.text.lower() == 'vingts' or token1.text.lower() == 'vingt' or token1.text.lower() == 'cent' or token1.text.lower() == 'cents':
        if token2.pos_ == "NOUN" or token2.text.lower() == "et":
            print("Liaison ok nb1 ",token1.text,'et',token2.text)
            return "O"
        else:
            print("Pas de liaison nb1",token1.text,'et',token2.text)
            return None

    # Après "un"
    elif token1.text.lower() == 'un':
        if token2.pos_ == 'NOUN' or token2.pos_ == 'ADJ':
            if token2.text.lower() not in mois :
                print("Liaison ok nb2 ", token1.text, 'et', token2.text)
                return "O"
            else:
                print("Pas de liaison nb2 ", token1.text, 'et', token2.text)
                return None
        else:
            print("Pas de liaison nb3 ", token1.text, 'et', token2.text)
            return None

    # Quand "neuf" rencontre les mots dans la liste, la prononciation sera changée
    elif token1.text.lower() == "neuf" and token2.text.lower() in mot_excep:
        print("La pronciation de "+token1.text+" est [nœv]") 
        return "N"

    # Quand le chiffre est employé comme pronom, on fait pas de liaision
    # EXemple : j'ai connu quatre personnes, et trois y demeurent toujours. dep_--> nsubj
    # Exemple : mon fils a eu un deux en anglais pos_--> PRON
    elif "subj" in token1.dep_ or token1.pos_ == "PRON" or "subj" in token2.dep_ or token2.pos_ == "PRON":
        print("Pas de liaison car 'PRON'",token1.text,'et',token2.text)
        return None
    else:
        if token1.text.lower() != "neuf":
249
250
            typeLiaison = obl_facul(token1,token2)
            return typeLiaison
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


# Fonction pour vérifier les règles grammaticales de liaison
# entrée : 2 tokens
# sortie : F ou O ou None
def obl_facul(token1, token2):

    # règles obligatoires
    reg_obl_cat = {'DET':['NOUN','ADJ','PRON'],'PRON':['PRON','VERB','AUX'],'ADJ':['NOUN'],'NUM':['NOUN','ADJ']}
    reg_obl_mot = {'quand':['PRON'],'très':['ADJ'],'trop':['ADJ'],'tout':['ADJ','ADP']}
    reg_obl_prep = ['dans','chez','en','sans','sous','dès','des','aux']

    # règles facultatives
    reg_fac_cat = {'NOUN':['ADJ'],'ADV':['ADJ'],'VERB':['VERB','ADJ','ADV','ADP','AUX']}
    reg_fac_mot = ['pas','mais','avoir','être']
    reg_fac_prep = ['après','avant','depuis','pendant']

    if 'Tense=Pres|VerbForm=Part' in token1.tag_:  # liaison après un participe présent
        return "F"

    elif token1.pos_ in reg_obl_cat.keys():
        reg_obl_cat_lis = reg_obl_cat[token1.pos_]  # la liste correspondante du tableau reg_obl_cat
        if token2.pos_ in reg_obl_cat_lis:
            print("Cas1 obligatoire : "+token1.pos_, token2.pos_)
            return "O"
        else:
            print("Pas de liaison1 : "+token1.pos_, token2.pos_)
            return None

    elif token1.text.lower() in reg_obl_mot.keys():
        reg_obl_mot_lis = reg_obl_mot[token1.text]  # la liste correspondante du tableau reg_obl_mot
        if token2.pos_ in reg_obl_mot_lis:
            print("Cas2 obligatoire : "+token1.text, token2.pos_)
            return "O"
        else:
            print("Pas de liaison2 : "+token1.pos_, token2.pos_)
            return None

    elif token1.pos_=='VERB' and token2.pos_ == 'PRON':
            print("Cas3 obligatoire : "+token1.pos_, token2.pos_)
            return "O"

    elif token1.lemma_ in reg_fac_mot:
        print("Cas1 facultatif : " + token1.pos_, token2.pos_)
        return "F"

    elif token1.pos_ in reg_fac_cat.keys():
        if token1.pos_ == 'NOUN' and 'Number=Plur' in token1.tag_:  # vérifier si le nom est au pluriel
            reg_fac_cat_lis = reg_fac_cat[token1.pos_]
            if token2.pos_ in reg_fac_cat_lis:
                print("Cas2 facultatif : " + token1.pos_, token2.pos_)
                return "F"
        elif token1.pos_ != 'NOUN': # cas des verbes ou des adverbes
            reg_fac_cat_lis = reg_fac_cat[token1.pos_]
            if token2.pos_ in reg_fac_cat_lis:
                print("Cas3 facultatif : " + token1.pos_, token2.pos_)
                return "F"     
        else:
            print("Pas de liaison3 : "+token1.pos_, token2.pos_)
            return None
    
    elif token1.pos_ == 'ADP':    # cas des prepositions 
        if token1.text.lower() in reg_obl_prep: 
            print ("Cas4 obligatoire : "+token1.text, token2.text)
            return "O"
        elif token1.text.lower() in reg_fac_prep:
            print ("Cas4 facultatif : "+token1.text, token2.text)
            return "F"
        else :
            print("Pas de liaison4 : " + token1.text, token2.text)
            return None
    else:
        print("Pas de liaison5 : "+token1.pos_, token2.pos_)
        return None


# Fonction pour intégrer toutes les fonctions établies pour nous déterminer la liaison
# entrée : 2 tokens
# sortie : F ou O ou None
def verifliaison(token1, token2):
    num = ['un', 'une', 'deux', 'trois', 'onze', 'onzième','onzièmes', 'neuf', 'vingt', 'vingts', 'cent', 'cents']

    if token1.text.lower() in num or token2.text.lower() in num:
        if exception(token1, token2):
            if gfr(token1):
336
                typeLiaison = liaison_nombre(token1, token2)
337
            else:
338
                typeLiaison = None
339
        else:
340
            typeLiaison = None
341
342
343
    else:
        if exception(token1,token2):
            if gfr(token1):
344
                typeLiaison = obl_facul(token1, token2)
345
            else:
346
                typeLiaison = None
347
        else:
348
349
            typeLiaison = None
    return typeLiaison
350
351
352
353
354
355


# Fonction pour determiner le phoneme de liaison
# en entree : une lettre
# en sortie : un str
def phon_liaison(lettre):
356
    l_p = {'s':'phon_z', 'x':'phon_z', 'z':'phon_z', 't':'phon_t', 'd':'phon_t', 'n':'phon_n', 'p':'phon_p', 'r':'phon_r_maj', 'g':'phon_g'}
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
    if lettre in l_p.keys():
        phon = l_p[lettre]
    else:
        phon = "rien"
    return phon


# Fonction pour la denasalisation
#entree : la transcription du mot (str)
#sortie : la transcription à jour (str)
def denasal (trans): 
    if trans[-1] == '̃' :
            trans = trans[:-1]
            trans += 'n'
    return trans