bazarr/libs/langdetect/utils/ngram.py

261 lines
10 KiB
Python

import re
import six
from . import messages
from .unicode_block import (
unicode_block,
UNICODE_BASIC_LATIN,
UNICODE_LATIN_1_SUPPLEMENT,
UNICODE_LATIN_EXTENDED_B,
UNICODE_GENERAL_PUNCTUATION,
UNICODE_ARABIC,
UNICODE_LATIN_EXTENDED_ADDITIONAL,
UNICODE_HIRAGANA,
UNICODE_KATAKANA,
UNICODE_BOPOMOFO,
UNICODE_BOPOMOFO_EXTENDED,
UNICODE_CJK_UNIFIED_IDEOGRAPHS,
UNICODE_HANGUL_SYLLABLES,
)
class NGram(object):
LATIN1_EXCLUDED = messages.get_string('NGram.LATIN1_EXCLUDE')
N_GRAM = 3
def __init__(self):
self.grams = ' '
self.capitalword = False
def add_char(self, ch):
'''Append a character into ngram buffer.'''
ch = self.normalize(ch)
last_char = self.grams[-1]
if last_char == ' ':
self.grams = ' '
self.capitalword = False
if ch == ' ':
return
elif len(self.grams) >= self.N_GRAM:
self.grams = self.grams[1:]
self.grams += ch
if ch.isupper():
if last_char.isupper():
self.capitalword = True
else:
self.capitalword = False
def get(self, n):
'''Get n-gram.'''
if self.capitalword:
return
if n < 1 or n > self.N_GRAM or len(self.grams) < n:
return
if n == 1:
ch = self.grams[-1]
if ch == ' ':
return
return ch
else:
return self.grams[-n:]
@classmethod
def normalize(cls, ch):
block = unicode_block(ch)
if block == UNICODE_BASIC_LATIN:
if ch < 'A' or ('Z' < ch < 'a') or 'z' < ch:
ch = ' '
elif block == UNICODE_LATIN_1_SUPPLEMENT:
if cls.LATIN1_EXCLUDED.find(ch) >= 0:
ch = ' '
elif block == UNICODE_LATIN_EXTENDED_B:
# normalization for Romanian
if ch == six.u('\u0219'): # Small S with comma below => with cedilla
ch = six.u('\u015f')
if ch == six.u('\u021b'): # Small T with comma below => with cedilla
ch = six.u('\u0163')
elif block == UNICODE_GENERAL_PUNCTUATION:
ch = ' '
elif block == UNICODE_ARABIC:
if ch == six.u('\u06cc'):
ch = six.u('\u064a') # Farsi yeh => Arabic yeh
elif block == UNICODE_LATIN_EXTENDED_ADDITIONAL:
if ch >= six.u('\u1ea0'):
ch = six.u('\u1ec3')
elif block == UNICODE_HIRAGANA:
ch = six.u('\u3042')
elif block == UNICODE_KATAKANA:
ch = six.u('\u30a2')
elif block in (UNICODE_BOPOMOFO, UNICODE_BOPOMOFO_EXTENDED):
ch = six.u('\u3105')
elif block == UNICODE_CJK_UNIFIED_IDEOGRAPHS:
ch = cls.CJK_MAP.get(ch, ch)
elif block == UNICODE_HANGUL_SYLLABLES:
ch = six.u('\uac00')
return ch
@classmethod
def normalize_vi(cls, text):
'''Normalizer for Vietnamese.
Normalize Alphabet + Diacritical Mark(U+03xx) into U+1Exx.
'''
def repl(m):
alphabet = cls.TO_NORMALIZE_VI_CHARS.find(m.group(1))
dmark = cls.DMARK_CLASS.find(m.group(2)) # Diacritical Mark
return cls.NORMALIZED_VI_CHARS[dmark][alphabet]
return cls.ALPHABET_WITH_DMARK.sub(repl, text)
NORMALIZED_VI_CHARS = [
messages.get_string('NORMALIZED_VI_CHARS_0300'),
messages.get_string('NORMALIZED_VI_CHARS_0301'),
messages.get_string('NORMALIZED_VI_CHARS_0303'),
messages.get_string('NORMALIZED_VI_CHARS_0309'),
messages.get_string('NORMALIZED_VI_CHARS_0323')]
TO_NORMALIZE_VI_CHARS = messages.get_string('TO_NORMALIZE_VI_CHARS')
DMARK_CLASS = messages.get_string('DMARK_CLASS')
ALPHABET_WITH_DMARK = re.compile(
'([' + TO_NORMALIZE_VI_CHARS + '])([' + DMARK_CLASS + '])',
re.UNICODE)
# CJK Kanji Normalization Mapping
CJK_CLASS = [
messages.get_string('NGram.KANJI_1_0'),
messages.get_string('NGram.KANJI_1_2'),
messages.get_string('NGram.KANJI_1_4'),
messages.get_string('NGram.KANJI_1_8'),
messages.get_string('NGram.KANJI_1_11'),
messages.get_string('NGram.KANJI_1_12'),
messages.get_string('NGram.KANJI_1_13'),
messages.get_string('NGram.KANJI_1_14'),
messages.get_string('NGram.KANJI_1_16'),
messages.get_string('NGram.KANJI_1_18'),
messages.get_string('NGram.KANJI_1_22'),
messages.get_string('NGram.KANJI_1_27'),
messages.get_string('NGram.KANJI_1_29'),
messages.get_string('NGram.KANJI_1_31'),
messages.get_string('NGram.KANJI_1_35'),
messages.get_string('NGram.KANJI_2_0'),
messages.get_string('NGram.KANJI_2_1'),
messages.get_string('NGram.KANJI_2_4'),
messages.get_string('NGram.KANJI_2_9'),
messages.get_string('NGram.KANJI_2_10'),
messages.get_string('NGram.KANJI_2_11'),
messages.get_string('NGram.KANJI_2_12'),
messages.get_string('NGram.KANJI_2_13'),
messages.get_string('NGram.KANJI_2_15'),
messages.get_string('NGram.KANJI_2_16'),
messages.get_string('NGram.KANJI_2_18'),
messages.get_string('NGram.KANJI_2_21'),
messages.get_string('NGram.KANJI_2_22'),
messages.get_string('NGram.KANJI_2_23'),
messages.get_string('NGram.KANJI_2_28'),
messages.get_string('NGram.KANJI_2_29'),
messages.get_string('NGram.KANJI_2_30'),
messages.get_string('NGram.KANJI_2_31'),
messages.get_string('NGram.KANJI_2_32'),
messages.get_string('NGram.KANJI_2_35'),
messages.get_string('NGram.KANJI_2_36'),
messages.get_string('NGram.KANJI_2_37'),
messages.get_string('NGram.KANJI_2_38'),
messages.get_string('NGram.KANJI_3_1'),
messages.get_string('NGram.KANJI_3_2'),
messages.get_string('NGram.KANJI_3_3'),
messages.get_string('NGram.KANJI_3_4'),
messages.get_string('NGram.KANJI_3_5'),
messages.get_string('NGram.KANJI_3_8'),
messages.get_string('NGram.KANJI_3_9'),
messages.get_string('NGram.KANJI_3_11'),
messages.get_string('NGram.KANJI_3_12'),
messages.get_string('NGram.KANJI_3_13'),
messages.get_string('NGram.KANJI_3_15'),
messages.get_string('NGram.KANJI_3_16'),
messages.get_string('NGram.KANJI_3_18'),
messages.get_string('NGram.KANJI_3_19'),
messages.get_string('NGram.KANJI_3_22'),
messages.get_string('NGram.KANJI_3_23'),
messages.get_string('NGram.KANJI_3_27'),
messages.get_string('NGram.KANJI_3_29'),
messages.get_string('NGram.KANJI_3_30'),
messages.get_string('NGram.KANJI_3_31'),
messages.get_string('NGram.KANJI_3_32'),
messages.get_string('NGram.KANJI_3_35'),
messages.get_string('NGram.KANJI_3_36'),
messages.get_string('NGram.KANJI_3_37'),
messages.get_string('NGram.KANJI_3_38'),
messages.get_string('NGram.KANJI_4_0'),
messages.get_string('NGram.KANJI_4_9'),
messages.get_string('NGram.KANJI_4_10'),
messages.get_string('NGram.KANJI_4_16'),
messages.get_string('NGram.KANJI_4_17'),
messages.get_string('NGram.KANJI_4_18'),
messages.get_string('NGram.KANJI_4_22'),
messages.get_string('NGram.KANJI_4_24'),
messages.get_string('NGram.KANJI_4_28'),
messages.get_string('NGram.KANJI_4_34'),
messages.get_string('NGram.KANJI_4_39'),
messages.get_string('NGram.KANJI_5_10'),
messages.get_string('NGram.KANJI_5_11'),
messages.get_string('NGram.KANJI_5_12'),
messages.get_string('NGram.KANJI_5_13'),
messages.get_string('NGram.KANJI_5_14'),
messages.get_string('NGram.KANJI_5_18'),
messages.get_string('NGram.KANJI_5_26'),
messages.get_string('NGram.KANJI_5_29'),
messages.get_string('NGram.KANJI_5_34'),
messages.get_string('NGram.KANJI_5_39'),
messages.get_string('NGram.KANJI_6_0'),
messages.get_string('NGram.KANJI_6_3'),
messages.get_string('NGram.KANJI_6_9'),
messages.get_string('NGram.KANJI_6_10'),
messages.get_string('NGram.KANJI_6_11'),
messages.get_string('NGram.KANJI_6_12'),
messages.get_string('NGram.KANJI_6_16'),
messages.get_string('NGram.KANJI_6_18'),
messages.get_string('NGram.KANJI_6_20'),
messages.get_string('NGram.KANJI_6_21'),
messages.get_string('NGram.KANJI_6_22'),
messages.get_string('NGram.KANJI_6_23'),
messages.get_string('NGram.KANJI_6_25'),
messages.get_string('NGram.KANJI_6_28'),
messages.get_string('NGram.KANJI_6_29'),
messages.get_string('NGram.KANJI_6_30'),
messages.get_string('NGram.KANJI_6_32'),
messages.get_string('NGram.KANJI_6_34'),
messages.get_string('NGram.KANJI_6_35'),
messages.get_string('NGram.KANJI_6_37'),
messages.get_string('NGram.KANJI_6_39'),
messages.get_string('NGram.KANJI_7_0'),
messages.get_string('NGram.KANJI_7_3'),
messages.get_string('NGram.KANJI_7_6'),
messages.get_string('NGram.KANJI_7_7'),
messages.get_string('NGram.KANJI_7_9'),
messages.get_string('NGram.KANJI_7_11'),
messages.get_string('NGram.KANJI_7_12'),
messages.get_string('NGram.KANJI_7_13'),
messages.get_string('NGram.KANJI_7_16'),
messages.get_string('NGram.KANJI_7_18'),
messages.get_string('NGram.KANJI_7_19'),
messages.get_string('NGram.KANJI_7_20'),
messages.get_string('NGram.KANJI_7_21'),
messages.get_string('NGram.KANJI_7_23'),
messages.get_string('NGram.KANJI_7_25'),
messages.get_string('NGram.KANJI_7_28'),
messages.get_string('NGram.KANJI_7_29'),
messages.get_string('NGram.KANJI_7_32'),
messages.get_string('NGram.KANJI_7_33'),
messages.get_string('NGram.KANJI_7_35'),
messages.get_string('NGram.KANJI_7_37')]
CJK_MAP = {}
@classmethod
def _init_cjk_map(cls):
for cjk_list in cls.CJK_CLASS:
representative = cjk_list[0]
for ch in cjk_list:
cls.CJK_MAP[ch] = representative
NGram._init_cjk_map()