Detectando el lenguaje de un texto

A veces puede resultar útil detectar el lenguaje de un texto, en los sistemas *NIX es habitual tener un directorio con listas de palabras habituales de varios idiomas, simplemente comparando la proporción de palabras de un texto que cae en esa lista a través de los distintos lenguajes da un número que parece bastante significativo acerca de que idioma se trata.

Por ejemplo, probando esta aproximación sobre el texto traducido (en Markdown, sin convertir a HTML) de la entrevista de Snowden para Der Spiegel muestra los siguientes resultados:

1
2
3
4
5
6
7
$ python lang.py entrevista-de-edward-snowden-para-der-spiegel.txt
entrevista-de-edward-snowden-para-der-spiegel.txt [2341]

58.61%  spanish
51.60%  galician
29.18%  american-english
29.09%  british-english

Hacerlo sobre el texto de la GPLv3 (en inglés) resulta en:

1
2
3
4
5
6
7
$ python lang.py LICENSE
LICENSE [5251]

98.36%  american-english
98.15%  british-english
12.82%  spanish
10.68%  galician

Si bien la aproximación es muy basta, parece que el resultado es aceptable.

El script usado para las pruebas fué este, está pensado para usar los diccionarios de "american-english", "british-english", "galician" y "spanish" pero se podría usar cualquiera, claro:

 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
#!/usr/bin/env python

import re
import os
import sys

# Define un color para mostrar los resultados
color = True
if color:
    colorfile = "\x1b[0;92m" # Verde
    colorend = "\x1b[0m"     # Fin del color
else:
    colorfile = colorend = ""

# Diccionarios a comprobar
dictionaries = ["american-english", "british-english", "galician", "spanish"]
path = "/usr/share/dict"

class LangReader:
    words = re.compile(r'\w+')

    def __init__(self, dicts):
        '''Carga los diccionarios con las palabras, una por línea, en minúsculas.'''
        self.dicts = {}
        for d in dicts:
            self.dicts[d] = set(map(lambda x: x.strip().lower(),
                                       open(d, "rt").read().strip().split("\n")))

    def get_file_props(self, f):
        return self.get_props(f.read())

    def get_percents(self, data):
        '''Devuelve el porcentaje de palabras de cada idioma.'''
        props, total = self.get_props(data)
        percents = {}
        for lang in props:
            percents[lang] = round((float(props[lang]) / total) * 100, 2)
        return percents


    def get_props(self, data):
        '''Devuelve el número de palabras que se corresponde
           con el diccionario de cada idioma.'''
        counters = {}
        total = 0
        words = map(lambda w: w.lower(), self.words.findall(data))

        for lang in self.dicts:
            ldict = self.dicts[lang]
            counter = 0
            for word in words:
                if word in ldict:
                    counter += 1

            counters[lang] = counter

        return counters, len(words)


if __name__ == "__main__":
    if len(sys.argv) == 1:
        print >>sys.stderr, "%s <file>" % sys.argv[0]
        exit(0)

    files = map(lambda x: open(x, "rt"), sys.argv[1:])
    os.chdir(path)

    ld = LangReader(dictionaries)
    first = True
    for f in files:

        if not first:
            print "\n"

        props, total = ld.get_file_props(f)
        if total > 0:
            print "%s%s%s [%i]\n" % (colorfile, f.name, colorend, total)
            for i in sorted(props,
                            lambda x, y: props[x].__cmp__(props[y]),
                            reverse = True):

                print "%5.2f%%  %s" % ((props[i] / float(total)) * 100, i)

        else:
            print "%s%s%s nothing found" % (colorfile, f.name, colorend)
        first = False
Dándole color a tcpdump » « Detectando errores al vuelo con emacs