Nidhal Baccouri commited on
Commit
5891228
·
unverified ·
2 Parent(s): 4522b30 4b6dfb9

Merge pull request #219 from hanlhan/feature/baidu

Browse files
deep_translator/__init__.py CHANGED
@@ -2,6 +2,7 @@
2
 
3
  __copyright__ = "Copyright (C) 2020 Nidhal Baccouri"
4
 
 
5
  from deep_translator.chatgpt import ChatGptTranslator
6
  from deep_translator.deepl import DeeplTranslator
7
  from deep_translator.detection import batch_detection, single_detection
@@ -31,6 +32,7 @@ __all__ = [
31
  "LibreTranslator",
32
  "PapagoTranslator",
33
  "ChatGptTranslator",
 
34
  "single_detection",
35
  "batch_detection",
36
  ]
 
2
 
3
  __copyright__ = "Copyright (C) 2020 Nidhal Baccouri"
4
 
5
+ from deep_translator.baidu import BaiduTranslator
6
  from deep_translator.chatgpt import ChatGptTranslator
7
  from deep_translator.deepl import DeeplTranslator
8
  from deep_translator.detection import batch_detection, single_detection
 
32
  "LibreTranslator",
33
  "PapagoTranslator",
34
  "ChatGptTranslator",
35
+ "BaiduTranslator",
36
  "single_detection",
37
  "batch_detection",
38
  ]
deep_translator/baidu.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ baidu translator API
3
+ """
4
+
5
+ __copyright__ = "Copyright (C) 2020 Nidhal Baccouri"
6
+
7
+ import hashlib
8
+ import os
9
+ import random
10
+ from typing import List, Optional
11
+
12
+ import requests
13
+
14
+ from deep_translator.base import BaseTranslator
15
+ from deep_translator.constants import (
16
+ BAIDU_APPID_ENV_VAR,
17
+ BAIDU_APPKEY_ENV_VAR,
18
+ BAIDU_LANGUAGE_TO_CODE,
19
+ BASE_URLS,
20
+ )
21
+ from deep_translator.exceptions import (
22
+ ApiKeyException,
23
+ BaiduAPIerror,
24
+ ServerException,
25
+ TranslationNotFound,
26
+ )
27
+ from deep_translator.validate import is_empty, is_input_valid
28
+
29
+
30
+ class BaiduTranslator(BaseTranslator):
31
+ """
32
+ class that wraps functions, which use the BaiduTranslator translator
33
+ under the hood to translate word(s)
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ source: str = "en",
39
+ target: str = "zh",
40
+ appid: Optional[str] = os.getenv(BAIDU_APPID_ENV_VAR, None),
41
+ appkey: Optional[str] = os.getenv(BAIDU_APPKEY_ENV_VAR, None),
42
+ **kwargs
43
+ ):
44
+ """
45
+ @param appid: your baidu cloud api appid.
46
+ Get one here: https://fanyi-api.baidu.com/choose
47
+ @param appkey: your baidu cloud api appkey.
48
+ @param source: source language
49
+ @param target: target language
50
+ """
51
+ if not appid:
52
+ raise ApiKeyException(env_var=BAIDU_APPID_ENV_VAR)
53
+
54
+ if not appkey:
55
+ raise ApiKeyException(env_var=BAIDU_APPKEY_ENV_VAR)
56
+
57
+ self.appid = appid
58
+ self.appkey = appkey
59
+ super().__init__(
60
+ base_url=BASE_URLS.get("BAIDU"),
61
+ source=source,
62
+ target=target,
63
+ languages=BAIDU_LANGUAGE_TO_CODE,
64
+ **kwargs
65
+ )
66
+
67
+ def translate(self, text: str, **kwargs) -> str:
68
+ """
69
+ @param text: text to translate
70
+ @return: translated text
71
+ """
72
+ if is_input_valid(text):
73
+ if self._same_source_target() or is_empty(text):
74
+ return text
75
+
76
+ # Create the request parameters.
77
+ salt = random.randint(32768, 65536)
78
+ sign = hashlib.md5(
79
+ (self.appid + text + str(salt) + self.appkey).encode("utf-8")
80
+ ).hexdigest()
81
+ headers = {"Content-Type": "application/x-www-form-urlencoded"}
82
+ payload = {
83
+ "appid": self.appid,
84
+ "q": text,
85
+ "from": self.source,
86
+ "to": self.target,
87
+ "salt": salt,
88
+ "sign": sign,
89
+ }
90
+
91
+ # Do the request and check the connection.
92
+ try:
93
+ response = requests.post(
94
+ self._base_url, params=payload, headers=headers
95
+ )
96
+ except ConnectionError:
97
+ raise ServerException(503)
98
+ if response.status_code != 200:
99
+ raise ServerException(response.status_code)
100
+ # Get the response and check is not empty.
101
+ res = response.json()
102
+ if not res:
103
+ raise TranslationNotFound(text)
104
+ # Process and return the response.
105
+ if "error_code" in res:
106
+ raise BaiduAPIerror(res["error_msg"])
107
+ if "trans_result" in res:
108
+ return "\n".join([s["dst"] for s in res["trans_result"]])
109
+ else:
110
+ raise TranslationNotFound(text)
111
+
112
+ def translate_file(self, path: str, **kwargs) -> str:
113
+ return self._translate_file(path, **kwargs)
114
+
115
+ def translate_batch(self, batch: List[str], **kwargs) -> List[str]:
116
+ """
117
+ @param batch: list of texts to translate
118
+ @return: list of translations
119
+ """
120
+ return self._translate_batch(batch, **kwargs)
deep_translator/constants.py CHANGED
@@ -7,6 +7,8 @@ LIBRE_ENV_VAR = "LIBRE_API_KEY"
7
  MSFT_ENV_VAR = "MICROSOFT_API_KEY"
8
  QCRI_ENV_VAR = "QCRI_API_KEY"
9
  YANDEX_ENV_VAR = "YANDEX_API_KEY"
 
 
10
 
11
 
12
  BASE_URLS = {
@@ -23,6 +25,7 @@ BASE_URLS = {
23
  "PAPAGO_API": "https://openapi.naver.com/v1/papago/n2mt",
24
  "LIBRE": "https://libretranslate.com/",
25
  "LIBRE_FREE": "https://libretranslate.de/",
 
26
  }
27
 
28
  GOOGLE_LANGUAGES_TO_CODES = {
@@ -605,3 +608,34 @@ LIBRE_LANGUAGES_TO_CODES = {
605
  "Turkish": "tr",
606
  "Vietnamese": "vi",
607
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  MSFT_ENV_VAR = "MICROSOFT_API_KEY"
8
  QCRI_ENV_VAR = "QCRI_API_KEY"
9
  YANDEX_ENV_VAR = "YANDEX_API_KEY"
10
+ BAIDU_APPID_ENV_VAR = "BAIDU_APPID"
11
+ BAIDU_APPKEY_ENV_VAR = "BAIDU_APPKEY"
12
 
13
 
14
  BASE_URLS = {
 
25
  "PAPAGO_API": "https://openapi.naver.com/v1/papago/n2mt",
26
  "LIBRE": "https://libretranslate.com/",
27
  "LIBRE_FREE": "https://libretranslate.de/",
28
+ "BAIDU": "https://fanyi-api.baidu.com/api/trans/vip/translate",
29
  }
30
 
31
  GOOGLE_LANGUAGES_TO_CODES = {
 
608
  "Turkish": "tr",
609
  "Vietnamese": "vi",
610
  }
611
+
612
+ BAIDU_LANGUAGE_TO_CODE = {
613
+ "arabic": "ara",
614
+ "bulgarian": "bul",
615
+ "chinese (classical)": "wyw",
616
+ "chinese (simplified)": "zh",
617
+ "chinese (traditional)": "cht",
618
+ "czech": "cs",
619
+ "danish": "dan",
620
+ "dutch": "nl",
621
+ "english": "en",
622
+ "estonian": "est",
623
+ "finnish": "fin",
624
+ "french": "fra",
625
+ "german": "de",
626
+ "greek": "el",
627
+ "hungarian": "hu",
628
+ "italian": "it",
629
+ "japanese": "jp",
630
+ "korean": "kor",
631
+ "polish": "pl",
632
+ "portuguese": "pt",
633
+ "romanian": "ro",
634
+ "russian": "ru",
635
+ "slovenian": "slo",
636
+ "spanish": "spa",
637
+ "swedish": "swe",
638
+ "thai": "th",
639
+ "vietnamese": "vie",
640
+ "yueyu": "yue",
641
+ }
deep_translator/exceptions.py CHANGED
@@ -180,3 +180,16 @@ class AuthorizationException(Exception):
180
  def __init__(self, api_key, *args):
181
  msg = "Unauthorized access with the api key " + api_key
182
  super().__init__(msg, *args)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  def __init__(self, api_key, *args):
181
  msg = "Unauthorized access with the api key " + api_key
182
  super().__init__(msg, *args)
183
+
184
+
185
+ class BaiduAPIerror(Exception):
186
+ """
187
+ exception thrown if Baidu API returns one of its errors
188
+ """
189
+
190
+ def __init__(self, api_message):
191
+ self.api_message = str(api_message)
192
+ self.message = "Baidu API returned the following error"
193
+
194
+ def __str__(self):
195
+ return "{}: {}".format(self.message, self.api_message)
docs/README.rst CHANGED
@@ -665,6 +665,66 @@ Libre Translator
665
  translated = LibreTranslator(source='auto', target='en').translate_file('path/to/file')
666
 
667
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
668
 
669
  Proxy usage
670
  -------------
 
665
  translated = LibreTranslator(source='auto', target='en').translate_file('path/to/file')
666
 
667
 
668
+ BaiduTranslator
669
+ -----------------
670
+
671
+ .. note::
672
+
673
+ In order to use the BaiduTranslator translator, you need to generate a secret_id and a secret_key.
674
+ deep-translator supports both Pro and free APIs. Just check the examples below.
675
+ Visit http://api.fanyi.baidu.com/product/113 for more information on how to generate your Baidu appid
676
+ and appkey.
677
+
678
+ - Simple translation
679
+
680
+ .. code-block:: python
681
+
682
+ text = 'Hello world'
683
+ translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate(text)
684
+
685
+ - Translate batch of texts
686
+
687
+ .. code-block:: python
688
+ =
689
+ texts = ["Hello world", "How are you?"]
690
+ translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate_batch(texts)
691
+
692
+ - Translate from a file:
693
+
694
+ .. code-block:: python
695
+
696
+ translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate_file('path/to/file')
697
+
698
+ BaiduTranslator
699
+ -----------------
700
+
701
+ .. note::
702
+
703
+ In order to use the BaiduTranslator translator, you need to generate a secret_id and a secret_key.
704
+ deep-translator supports both Pro and free APIs. Just check the examples below.
705
+ Visit http://api.fanyi.baidu.com/product/113 for more information on how to generate your Baidu appid
706
+ and appkey.
707
+
708
+ - Simple translation
709
+
710
+ .. code-block:: python
711
+
712
+ text = 'Hello world'
713
+ translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate(text)
714
+
715
+ - Translate batch of texts
716
+
717
+ .. code-block:: python
718
+ =
719
+ texts = ["Hello world", "How are you?"]
720
+ translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate_batch(texts)
721
+
722
+ - Translate from a file:
723
+
724
+ .. code-block:: python
725
+
726
+ translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate_file('path/to/file')
727
+
728
 
729
  Proxy usage
730
  -------------
tests/test_baidu.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from unittest.mock import Mock, patch
2
+
3
+ import pytest
4
+
5
+ from deep_translator import BaiduTranslator
6
+ from deep_translator.exceptions import BaiduAPIerror
7
+
8
+
9
+ @patch("deep_translator.baidu.requests")
10
+ def test_simple_translation(mock_requests):
11
+ translator = BaiduTranslator(
12
+ appid="this-is-an-valid-appid",
13
+ appkey="this-is-an-valid-appkey",
14
+ source="en",
15
+ target="zh",
16
+ )
17
+ # Set the request response mock.
18
+ mock_response = Mock()
19
+ mock_response.status_code = 200
20
+ mock_response.json.return_value = {
21
+ "from": "en",
22
+ "to": "zh",
23
+ "trans_result": [{"src": "hello", "dst": "你好"}],
24
+ }
25
+ mock_requests.post.return_value = mock_response
26
+ translation = translator.translate("hello")
27
+ assert translation == "你好"
28
+
29
+
30
+ @patch("deep_translator.baidu.requests.get")
31
+ def test_wrong_api_key(mock_requests):
32
+ translator = BaiduTranslator(
33
+ appid="this-is-a-wrong-appid",
34
+ appkey="this-is-a-wrong-appkey",
35
+ source="en",
36
+ target="zh",
37
+ )
38
+ # Set the response status_code only.
39
+ mock_response = Mock()
40
+ mock_response.status_code = 200
41
+ mock_response.json.return_value = {
42
+ "error_code": "54001",
43
+ "error_msg": "Invalid Sign",
44
+ }
45
+ mock_requests.post.return_value = mock_response
46
+ with pytest.raises(BaiduAPIerror):
47
+ translator.translate("Hello")
48
+
49
+
50
+ # the remaining tests are actual requests to Baidu translator API and use appid and appkey
51
+ # if appid and appkey variable is None, they are skipped
52
+
53
+ appid = None
54
+ appkey = None
55
+
56
+
57
+ @pytest.mark.skipif(
58
+ appid is None or appkey is None,
59
+ reason="appid or appkey is not provided",
60
+ )
61
+ def test_baidu_successful_post_onetarget():
62
+ posted = BaiduTranslator(
63
+ appid=appid, appkey=appkey, source="en", target="zh"
64
+ ).translate("Hello! How are you?")
65
+ assert isinstance(posted, str)