-
-
Notifications
You must be signed in to change notification settings - Fork 190
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #219 from hanlhan/feature/baidu
added baidu support
- Loading branch information
Showing
6 changed files
with
294 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
""" | ||
baidu translator API | ||
""" | ||
|
||
__copyright__ = "Copyright (C) 2020 Nidhal Baccouri" | ||
|
||
import hashlib | ||
import os | ||
import random | ||
from typing import List, Optional | ||
|
||
import requests | ||
|
||
from deep_translator.base import BaseTranslator | ||
from deep_translator.constants import ( | ||
BAIDU_APPID_ENV_VAR, | ||
BAIDU_APPKEY_ENV_VAR, | ||
BAIDU_LANGUAGE_TO_CODE, | ||
BASE_URLS, | ||
) | ||
from deep_translator.exceptions import ( | ||
ApiKeyException, | ||
BaiduAPIerror, | ||
ServerException, | ||
TranslationNotFound, | ||
) | ||
from deep_translator.validate import is_empty, is_input_valid | ||
|
||
|
||
class BaiduTranslator(BaseTranslator): | ||
""" | ||
class that wraps functions, which use the BaiduTranslator translator | ||
under the hood to translate word(s) | ||
""" | ||
|
||
def __init__( | ||
self, | ||
source: str = "en", | ||
target: str = "zh", | ||
appid: Optional[str] = os.getenv(BAIDU_APPID_ENV_VAR, None), | ||
appkey: Optional[str] = os.getenv(BAIDU_APPKEY_ENV_VAR, None), | ||
**kwargs | ||
): | ||
""" | ||
@param appid: your baidu cloud api appid. | ||
Get one here: https://fanyi-api.baidu.com/choose | ||
@param appkey: your baidu cloud api appkey. | ||
@param source: source language | ||
@param target: target language | ||
""" | ||
if not appid: | ||
raise ApiKeyException(env_var=BAIDU_APPID_ENV_VAR) | ||
|
||
if not appkey: | ||
raise ApiKeyException(env_var=BAIDU_APPKEY_ENV_VAR) | ||
|
||
self.appid = appid | ||
self.appkey = appkey | ||
super().__init__( | ||
base_url=BASE_URLS.get("BAIDU"), | ||
source=source, | ||
target=target, | ||
languages=BAIDU_LANGUAGE_TO_CODE, | ||
**kwargs | ||
) | ||
|
||
def translate(self, text: str, **kwargs) -> str: | ||
""" | ||
@param text: text to translate | ||
@return: translated text | ||
""" | ||
if is_input_valid(text): | ||
if self._same_source_target() or is_empty(text): | ||
return text | ||
|
||
# Create the request parameters. | ||
salt = random.randint(32768, 65536) | ||
sign = hashlib.md5( | ||
(self.appid + text + str(salt) + self.appkey).encode("utf-8") | ||
).hexdigest() | ||
headers = {"Content-Type": "application/x-www-form-urlencoded"} | ||
payload = { | ||
"appid": self.appid, | ||
"q": text, | ||
"from": self.source, | ||
"to": self.target, | ||
"salt": salt, | ||
"sign": sign, | ||
} | ||
|
||
# Do the request and check the connection. | ||
try: | ||
response = requests.post( | ||
self._base_url, params=payload, headers=headers | ||
) | ||
except ConnectionError: | ||
raise ServerException(503) | ||
if response.status_code != 200: | ||
raise ServerException(response.status_code) | ||
# Get the response and check is not empty. | ||
res = response.json() | ||
if not res: | ||
raise TranslationNotFound(text) | ||
# Process and return the response. | ||
if "error_code" in res: | ||
raise BaiduAPIerror(res["error_msg"]) | ||
if "trans_result" in res: | ||
return "\n".join([s["dst"] for s in res["trans_result"]]) | ||
else: | ||
raise TranslationNotFound(text) | ||
|
||
def translate_file(self, path: str, **kwargs) -> str: | ||
return self._translate_file(path, **kwargs) | ||
|
||
def translate_batch(self, batch: List[str], **kwargs) -> List[str]: | ||
""" | ||
@param batch: list of texts to translate | ||
@return: list of translations | ||
""" | ||
return self._translate_batch(batch, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
from unittest.mock import Mock, patch | ||
|
||
import pytest | ||
|
||
from deep_translator import BaiduTranslator | ||
from deep_translator.exceptions import BaiduAPIerror | ||
|
||
|
||
@patch("deep_translator.baidu.requests") | ||
def test_simple_translation(mock_requests): | ||
translator = BaiduTranslator( | ||
appid="this-is-an-valid-appid", | ||
appkey="this-is-an-valid-appkey", | ||
source="en", | ||
target="zh", | ||
) | ||
# Set the request response mock. | ||
mock_response = Mock() | ||
mock_response.status_code = 200 | ||
mock_response.json.return_value = { | ||
"from": "en", | ||
"to": "zh", | ||
"trans_result": [{"src": "hello", "dst": "你好"}], | ||
} | ||
mock_requests.post.return_value = mock_response | ||
translation = translator.translate("hello") | ||
assert translation == "你好" | ||
|
||
|
||
@patch("deep_translator.baidu.requests.get") | ||
def test_wrong_api_key(mock_requests): | ||
translator = BaiduTranslator( | ||
appid="this-is-a-wrong-appid", | ||
appkey="this-is-a-wrong-appkey", | ||
source="en", | ||
target="zh", | ||
) | ||
# Set the response status_code only. | ||
mock_response = Mock() | ||
mock_response.status_code = 200 | ||
mock_response.json.return_value = { | ||
"error_code": "54001", | ||
"error_msg": "Invalid Sign", | ||
} | ||
mock_requests.post.return_value = mock_response | ||
with pytest.raises(BaiduAPIerror): | ||
translator.translate("Hello") | ||
|
||
|
||
# the remaining tests are actual requests to Baidu translator API and use appid and appkey | ||
# if appid and appkey variable is None, they are skipped | ||
|
||
appid = None | ||
appkey = None | ||
|
||
|
||
@pytest.mark.skipif( | ||
appid is None or appkey is None, | ||
reason="appid or appkey is not provided", | ||
) | ||
def test_baidu_successful_post_onetarget(): | ||
posted = BaiduTranslator( | ||
appid=appid, appkey=appkey, source="en", target="zh" | ||
).translate("Hello! How are you?") | ||
assert isinstance(posted, str) |