fast-langdetect 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 LLM Kira
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.1
2
+ Name: fast-langdetect
3
+ Version: 0.1.0
4
+ Summary: Quickly detect text language and segment language
5
+ Author-Email: sudoskys <coldlando@hotmail.com>
6
+ License: MIT
7
+ Requires-Python: <3.12,>=3.8
8
+ Requires-Dist: fasttext-wheel>=0.9.2
9
+ Requires-Dist: requests>=2.31.0
10
+ Requires-Dist: robust-downloader>=0.0.2
11
+ Description-Content-Type: text/markdown
12
+
13
+ # fast-langdetect
14
+
15
+ [![PyPI version](https://badge.fury.io/py/fast-langdetect.svg)](https://badge.fury.io/py/fast-langdetect)
16
+ [![Downloads](https://pepy.tech/badge/fast-langdetect)](https://pepy.tech/project/fast-langdetect)
17
+ [![Downloads](https://pepy.tech/badge/fast-langdetect/month)](https://pepy.tech/project/fast-langdetect/month)
18
+
19
+ Python 3.8-3.11 support only.
20
+
21
+ 80x faster and 95% accurate language identification with Fasttext
22
+
23
+ This library is a wrapper for the language detection model trained on fasttext by Facebook. For more information, please
24
+ visit: https://fasttext.cc/docs/en/language-identification.html
25
+
26
+ This repository is patched
27
+ from [zafercavdar/fasttext-langdetect](https://github.com/zafercavdar/fasttext-langdetect#benchmark), adding
28
+ multi-language segmentation and better packaging
29
+ support.
30
+
31
+ Facilitates more accurate TTS implementation.
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install fast-langdetect
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ **For more accurate language detection, please use `detect(text,low_memory=False)` to load the big model.**
42
+
43
+ **Model will be downloaded in `/tmp/fasttext-langdetect` directory when you first use it.**
44
+
45
+ ```python
46
+ from fast_langdetect import detect_langs
47
+
48
+ print(detect_langs("Hello, world!"))
49
+ # [en:0.9999961853027344]
50
+
51
+ print(detect_langs("Привет, мир!"))
52
+ # [ru:0.9999961853027344]
53
+
54
+
55
+ print(detect_langs("你好,世界!"))
56
+ # [zh:0.9999961853027344]
57
+
58
+ ```
59
+
60
+ ## Advanced usage
61
+
62
+ ```python
63
+ from fast_langdetect import detect, parse_sentence, detect_multilingual
64
+
65
+ print(detect("Hello, world!"))
66
+ # {'lang': 'en', 'score': 0.1520957201719284}
67
+
68
+ print(detect_multilingual("Hello, world!你好世界!Привет, мир!"))
69
+ # [{'lang': 'ru', 'score': 0.39008623361587524}, {'lang': 'zh', 'score': 0.18235979974269867}, {'lang': 'ja', 'score': 0.08473210036754608}, {'lang': 'sr', 'score': 0.057975586503744125}, {'lang': 'en', 'score': 0.05422825738787651}]
70
+
71
+ print(parse_sentence("你好世界!Hello, world!Привет, мир!"))
72
+ # [{'text': '你好世界!Hello, world!', 'lang': 'ZH', 'length': 18}, {'text': 'Привет, мир!', 'lang': 'UK', 'length': 12}, {'text': '', 'lang': 'EN', 'length': 0}]
73
+ ```
74
+
75
+ ## Accuracy
76
+
77
+ References to the [benchmark](https://github.com/zafercavdar/fasttext-langdetect#benchmark)
@@ -0,0 +1,65 @@
1
+ # fast-langdetect
2
+
3
+ [![PyPI version](https://badge.fury.io/py/fast-langdetect.svg)](https://badge.fury.io/py/fast-langdetect)
4
+ [![Downloads](https://pepy.tech/badge/fast-langdetect)](https://pepy.tech/project/fast-langdetect)
5
+ [![Downloads](https://pepy.tech/badge/fast-langdetect/month)](https://pepy.tech/project/fast-langdetect/month)
6
+
7
+ Python 3.8-3.11 support only.
8
+
9
+ 80x faster and 95% accurate language identification with Fasttext
10
+
11
+ This library is a wrapper for the language detection model trained on fasttext by Facebook. For more information, please
12
+ visit: https://fasttext.cc/docs/en/language-identification.html
13
+
14
+ This repository is patched
15
+ from [zafercavdar/fasttext-langdetect](https://github.com/zafercavdar/fasttext-langdetect#benchmark), adding
16
+ multi-language segmentation and better packaging
17
+ support.
18
+
19
+ Facilitates more accurate TTS implementation.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install fast-langdetect
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ **For more accurate language detection, please use `detect(text,low_memory=False)` to load the big model.**
30
+
31
+ **Model will be downloaded in `/tmp/fasttext-langdetect` directory when you first use it.**
32
+
33
+ ```python
34
+ from fast_langdetect import detect_langs
35
+
36
+ print(detect_langs("Hello, world!"))
37
+ # [en:0.9999961853027344]
38
+
39
+ print(detect_langs("Привет, мир!"))
40
+ # [ru:0.9999961853027344]
41
+
42
+
43
+ print(detect_langs("你好,世界!"))
44
+ # [zh:0.9999961853027344]
45
+
46
+ ```
47
+
48
+ ## Advanced usage
49
+
50
+ ```python
51
+ from fast_langdetect import detect, parse_sentence, detect_multilingual
52
+
53
+ print(detect("Hello, world!"))
54
+ # {'lang': 'en', 'score': 0.1520957201719284}
55
+
56
+ print(detect_multilingual("Hello, world!你好世界!Привет, мир!"))
57
+ # [{'lang': 'ru', 'score': 0.39008623361587524}, {'lang': 'zh', 'score': 0.18235979974269867}, {'lang': 'ja', 'score': 0.08473210036754608}, {'lang': 'sr', 'score': 0.057975586503744125}, {'lang': 'en', 'score': 0.05422825738787651}]
58
+
59
+ print(parse_sentence("你好世界!Hello, world!Привет, мир!"))
60
+ # [{'text': '你好世界!Hello, world!', 'lang': 'ZH', 'length': 18}, {'text': 'Привет, мир!', 'lang': 'UK', 'length': 12}, {'text': '', 'lang': 'EN', 'length': 0}]
61
+ ```
62
+
63
+ ## Accuracy
64
+
65
+ References to the [benchmark](https://github.com/zafercavdar/fasttext-langdetect#benchmark)
@@ -0,0 +1,31 @@
1
+ [project]
2
+ name = "fast-langdetect"
3
+ version = "0.1.0"
4
+ description = "Quickly detect text language and segment language"
5
+ authors = [
6
+ { name = "sudoskys", email = "coldlando@hotmail.com" },
7
+ ]
8
+ dependencies = [
9
+ "fasttext-wheel>=0.9.2",
10
+ "requests>=2.31.0",
11
+ "robust-downloader>=0.0.2",
12
+ ]
13
+ requires-python = ">=3.8,<3.12"
14
+ readme = "README.md"
15
+
16
+ [project.license]
17
+ text = "MIT"
18
+
19
+ [build-system]
20
+ requires = [
21
+ "pdm-backend",
22
+ ]
23
+ build-backend = "pdm.backend"
24
+
25
+ [tool.pdm]
26
+ package-type = "library"
27
+
28
+ [tool.pdm.dev-dependencies]
29
+ dev = [
30
+ "pytest>=7.4.4",
31
+ ]
@@ -0,0 +1,4 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .ft_detect import detect, detect_langs, detect_multilingual # noqa: F401
4
+ from .split import parse_sentence # noqa: F401
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Zafer Çavdar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Time : 2024/1/17 下午4:00
3
+ # @Author : sudoskys
4
+ # @File : __init__.py
5
+ from .infer import detect
6
+ from .infer import detect_multilingual # noqa: F401
7
+
8
+
9
+ def is_japanese(string):
10
+ for ch in string:
11
+ if 0x3040 < ord(ch) < 0x30FF:
12
+ return True
13
+ return False
14
+
15
+
16
+ def detect_langs(sentence, *, low_memory: bool = True):
17
+ """
18
+ Detect language
19
+ :param sentence: str sentence
20
+ :param low_memory: bool (default: True) whether to use low memory mode
21
+ :return: ZH, EN, JA, KO, FR, DE, ES, .... (two uppercase letters)
22
+ """
23
+ lang_code = detect(sentence, low_memory=low_memory).get("lang").upper()
24
+ if lang_code == "JA" and not is_japanese(sentence):
25
+ lang_code = "ZH"
26
+ return lang_code
@@ -0,0 +1,107 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Time : 2024/1/17 下午8:30
3
+ # @Author : sudoskys
4
+ # @File : infer.py
5
+ # @Software: PyCharm
6
+ import logging
7
+ import os
8
+ from pathlib import Path
9
+ from typing import Dict, Union, List
10
+
11
+ import fasttext
12
+ from robust_downloader import download
13
+
14
+ logger = logging.getLogger(__name__)
15
+ MODELS = {"low_mem": None, "high_mem": None}
16
+ FTLANG_CACHE = os.getenv("FTLANG_CACHE", "/tmp/fasttext-langdetect")
17
+
18
+ try:
19
+ # silences warnings as the package does not properly use the python 'warnings' package
20
+ # see https://github.com/facebookresearch/fastText/issues/1056
21
+ fasttext.FastText.eprint = lambda *args, **kwargs: None
22
+ except Exception:
23
+ pass
24
+
25
+
26
+ class DetectError(Exception):
27
+ pass
28
+
29
+
30
+ def get_model_map(low_memory=False):
31
+ """
32
+ Getting model map
33
+ :param low_memory:
34
+ :return:
35
+ """
36
+ if low_memory:
37
+ return "low_mem", FTLANG_CACHE, "lid.176.ftz", "https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.ftz"
38
+ else:
39
+ return "high_mem", FTLANG_CACHE, "lid.176.bin", "https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.bin"
40
+
41
+
42
+ def get_model_loaded(
43
+ low_memory: bool = False,
44
+ download_proxy: str = None
45
+ ):
46
+ """
47
+ Getting model loaded
48
+ :param low_memory:
49
+ :param download_proxy:
50
+ :return:
51
+ """
52
+ mode, cache, name, url = get_model_map(low_memory)
53
+ loaded = MODELS.get(mode, None)
54
+ if loaded:
55
+ return loaded
56
+ model_path = os.path.join(cache, name)
57
+ if Path(model_path).exists():
58
+ if Path(model_path).is_dir():
59
+ raise Exception(f"{model_path} is a directory")
60
+ try:
61
+ loaded_model = fasttext.load_model(model_path)
62
+ MODELS[mode] = loaded_model
63
+ except Exception as e:
64
+ logger.error(f"Error loading model {model_path}: {e}")
65
+ download(url=url, folder=cache, filename=name, proxy=download_proxy)
66
+ raise e
67
+ else:
68
+ return loaded_model
69
+
70
+ download(url=url, folder=cache, filename=name, proxy=download_proxy, retry_max=3, timeout=20)
71
+ loaded_model = fasttext.load_model(model_path)
72
+ MODELS[mode] = loaded_model
73
+ return loaded_model
74
+
75
+
76
+ def detect(text: str, *,
77
+ low_memory: bool = True,
78
+ model_download_proxy: str = None
79
+ ) -> Dict[str, Union[str, float]]:
80
+ model = get_model_loaded(low_memory=low_memory, download_proxy=model_download_proxy)
81
+ labels, scores = model.predict(text)
82
+ label = labels[0].replace("__label__", '')
83
+ score = min(float(scores[0]), 1.0)
84
+ return {
85
+ "lang": label,
86
+ "score": score,
87
+ }
88
+
89
+
90
+ def detect_multilingual(text: str, *,
91
+ low_memory: bool = True,
92
+ model_download_proxy: str = None,
93
+ k: int = 5,
94
+ threshold: float = 0.0,
95
+ on_unicode_error: str = "strict"
96
+ ) -> List[dict]:
97
+ model = get_model_loaded(low_memory=low_memory, download_proxy=model_download_proxy)
98
+ labels, scores = model.predict(text=text, k=k, threshold=threshold, on_unicode_error=on_unicode_error)
99
+ detect_result = []
100
+ for label, score in zip(labels, scores):
101
+ label = label.replace("__label__", '')
102
+ score = min(float(score), 1.0)
103
+ detect_result.append({
104
+ "lang": label,
105
+ "score": score,
106
+ })
107
+ return sorted(detect_result, key=lambda i: i['score'], reverse=True)
@@ -0,0 +1,66 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Time : 2024/1/17 下午4:23
3
+ # @Author : sudoskys
4
+ # @File : __init__.py.py
5
+ from typing import List
6
+
7
+ from .cut import CutSentence
8
+ from ..ft_detect import detect_langs
9
+
10
+ CUT = CutSentence()
11
+
12
+
13
+ def _merge_cell(result: List[dict]) -> List[dict]:
14
+ _merged = []
15
+ _cache = []
16
+ last_lang = None
17
+ for _result in result:
18
+ if _result["lang"] == last_lang:
19
+ _cache.append(_result["text"])
20
+ else:
21
+ if _cache:
22
+ _length = sum([len(_c) for _c in _cache])
23
+ _merged.append({"text": "".join(_cache), "lang": last_lang, "length": _length})
24
+ _cache = [_result["text"]]
25
+ last_lang = _result["lang"]
26
+ if _cache:
27
+ _length = sum([len(_c) for _c in _cache])
28
+ _merged.append({"text": "".join(_cache), "lang": last_lang, "length": _length})
29
+ return _merged
30
+
31
+
32
+ def parse_sentence(sentence: str,
33
+ merge_same: bool = True,
34
+ cell_limit: int = 150,
35
+ filter_space: bool = True,
36
+ low_memory: bool = True
37
+ ) -> list:
38
+ """
39
+ Parse sentence
40
+ :param sentence:
41
+ :param merge_same:
42
+ :param cell_limit:
43
+ :param filter_space:
44
+ :param low_memory:
45
+ :return:
46
+ """
47
+
48
+ cut_list = CUT.chinese_sentence_cut(sentence)
49
+ _cut_list = []
50
+ for _cut in cut_list:
51
+ if len(_cut) > cell_limit:
52
+ _text_list = [_cut[i:i + cell_limit] for i in range(0, len(_cut), cell_limit)]
53
+ _cut_list.extend(_text_list)
54
+ else:
55
+ _cut_list.append(_cut)
56
+ _result = []
57
+ for _cut in _cut_list:
58
+ _lang = detect_langs(_cut, low_memory=low_memory)
59
+ if not filter_space:
60
+ _result.append({"text": _cut, "lang": _lang, "length": len(_cut)})
61
+ else:
62
+ if _lang:
63
+ _result.append({"text": _cut, "lang": _lang, "length": len(_cut)})
64
+ if merge_same:
65
+ _result = _merge_cell(_result)
66
+ return _result
@@ -0,0 +1,59 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Time : 2024/1/17 下午8:27
3
+ # @Author : sudoskys
4
+ # @File : cut.py
5
+ # @Software: PyCharm
6
+ import re
7
+
8
+
9
+ class CutSentence(object):
10
+ @staticmethod
11
+ def english_sentence_cut(text) -> list:
12
+ list_ = list()
13
+ for s_str in text.split('.'):
14
+ if '?' in s_str:
15
+ list_.extend(s_str.split('?'))
16
+ elif '!' in s_str:
17
+ list_.extend(s_str.split('!'))
18
+ else:
19
+ list_.append(s_str)
20
+ return list_
21
+
22
+ @staticmethod
23
+ def chinese_sentence_cut(text) -> list:
24
+ """
25
+ 中文断句
26
+ """
27
+ text = re.sub('([^\n])([.!?。!?]+)(?!\d|[a-zA-Z]|[^\s\u4e00-\u9fa5\u3040-\u309f\u30a0-\u30ff\uac00-\ud7a3])',
28
+ r'\1\2\n', text) # 根据句子末尾的标点符号进行切分
29
+ text = re.sub('([。::!?\?])([^’”])', r'\1\n\2', text) # 根据中文断句符号进行切分
30
+ text = re.sub('(\.{6})([^’”])', r'\1\n\2', text) # 普通断句符号且后面没有引号
31
+ text = re.sub('(\…{2})([^’”])', r'\1\n\2', text) # 英文省略号且后面没有引号
32
+ text = re.sub('([.。!?\?\.{6}\…{2}][’”])([^’”])', r'\1\n\2', text) # 中文省略号且后面没有引号
33
+ text = re.sub('(\. )([^a-zA-Z\d])', r'\1\n\2', text) # 根据英文断句号+空格进行切分
34
+ text = re.sub('\n\n+', '\n', text) # 删除多余的换行符
35
+ return text.split("\n") # 断句号+引号且后面没有引号
36
+
37
+ def cut_chinese_sentence(self, text):
38
+ """
39
+ 中文断句
40
+ """
41
+ p = re.compile("“.*?”")
42
+ listr = []
43
+ index = 0
44
+ for i in p.finditer(text):
45
+ temp = ''
46
+ start = i.start()
47
+ end = i.end()
48
+ for j in range(index, start):
49
+ temp += text[j]
50
+ if temp != '':
51
+ temp_list = self.chinese_sentence_cut(temp)
52
+ listr += temp_list
53
+ temp = ''
54
+ for k in range(start, end):
55
+ temp += text[k]
56
+ if temp != ' ':
57
+ listr.append(temp)
58
+ index = end
59
+ return listr
@@ -0,0 +1,3 @@
1
+
2
+
3
+
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Time : 2024/1/17 下午5:28
3
+ # @Author : sudoskys
4
+ # @File : test_detect.py
5
+ # @Software: PyCharm
6
+
7
+
8
+ def test_muti_detect():
9
+ from fast_langdetect.ft_detect import detect_multilingual
10
+ result = detect_multilingual("hello world", low_memory=True)
11
+ assert result[0].get("lang") == "en", "ft_detect error"
12
+
13
+
14
+ def test_detect_totally():
15
+ from fast_langdetect import detect_langs
16
+ assert detect_langs("hello world") == "EN", "ft_detect error"
17
+ assert detect_langs("你好世界") == "ZH", "ft_detect error"
18
+ assert detect_langs("こんにちは世界") == "JA", "ft_detect error"
19
+
20
+
21
+ def test_parse():
22
+ from fast_langdetect import parse_sentence
23
+ assert parse_sentence("hello world") == [{"text": "hello world", "lang": "EN", "length": 11}], "ft_detect error"