Skip to content

Commit

Permalink
Merge pull request #219 from hanlhan/feature/baidu
Browse files Browse the repository at this point in the history
added baidu support
  • Loading branch information
nidhaloff authored Jun 28, 2023
2 parents 4522b30 + 4b6dfb9 commit 5891228
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 0 deletions.
2 changes: 2 additions & 0 deletions deep_translator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

__copyright__ = "Copyright (C) 2020 Nidhal Baccouri"

from deep_translator.baidu import BaiduTranslator
from deep_translator.chatgpt import ChatGptTranslator
from deep_translator.deepl import DeeplTranslator
from deep_translator.detection import batch_detection, single_detection
Expand Down Expand Up @@ -31,6 +32,7 @@
"LibreTranslator",
"PapagoTranslator",
"ChatGptTranslator",
"BaiduTranslator",
"single_detection",
"batch_detection",
]
120 changes: 120 additions & 0 deletions deep_translator/baidu.py
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)
34 changes: 34 additions & 0 deletions deep_translator/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
MSFT_ENV_VAR = "MICROSOFT_API_KEY"
QCRI_ENV_VAR = "QCRI_API_KEY"
YANDEX_ENV_VAR = "YANDEX_API_KEY"
BAIDU_APPID_ENV_VAR = "BAIDU_APPID"
BAIDU_APPKEY_ENV_VAR = "BAIDU_APPKEY"


BASE_URLS = {
Expand All @@ -23,6 +25,7 @@
"PAPAGO_API": "https://openapi.naver.com/v1/papago/n2mt",
"LIBRE": "https://libretranslate.com/",
"LIBRE_FREE": "https://libretranslate.de/",
"BAIDU": "https://fanyi-api.baidu.com/api/trans/vip/translate",
}

GOOGLE_LANGUAGES_TO_CODES = {
Expand Down Expand Up @@ -605,3 +608,34 @@
"Turkish": "tr",
"Vietnamese": "vi",
}

BAIDU_LANGUAGE_TO_CODE = {
"arabic": "ara",
"bulgarian": "bul",
"chinese (classical)": "wyw",
"chinese (simplified)": "zh",
"chinese (traditional)": "cht",
"czech": "cs",
"danish": "dan",
"dutch": "nl",
"english": "en",
"estonian": "est",
"finnish": "fin",
"french": "fra",
"german": "de",
"greek": "el",
"hungarian": "hu",
"italian": "it",
"japanese": "jp",
"korean": "kor",
"polish": "pl",
"portuguese": "pt",
"romanian": "ro",
"russian": "ru",
"slovenian": "slo",
"spanish": "spa",
"swedish": "swe",
"thai": "th",
"vietnamese": "vie",
"yueyu": "yue",
}
13 changes: 13 additions & 0 deletions deep_translator/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,16 @@ class AuthorizationException(Exception):
def __init__(self, api_key, *args):
msg = "Unauthorized access with the api key " + api_key
super().__init__(msg, *args)


class BaiduAPIerror(Exception):
"""
exception thrown if Baidu API returns one of its errors
"""

def __init__(self, api_message):
self.api_message = str(api_message)
self.message = "Baidu API returned the following error"

def __str__(self):
return "{}: {}".format(self.message, self.api_message)
60 changes: 60 additions & 0 deletions docs/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,66 @@ Libre Translator
translated = LibreTranslator(source='auto', target='en').translate_file('path/to/file')
BaiduTranslator
-----------------

.. note::

In order to use the BaiduTranslator translator, you need to generate a secret_id and a secret_key.
deep-translator supports both Pro and free APIs. Just check the examples below.
Visit http://api.fanyi.baidu.com/product/113 for more information on how to generate your Baidu appid
and appkey.

- Simple translation

.. code-block:: python
text = 'Hello world'
translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate(text)
- Translate batch of texts

.. code-block:: python
=
texts = ["Hello world", "How are you?"]
translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate_batch(texts)

- Translate from a file:

.. code-block:: python
translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate_file('path/to/file')
BaiduTranslator
-----------------

.. note::

In order to use the BaiduTranslator translator, you need to generate a secret_id and a secret_key.
deep-translator supports both Pro and free APIs. Just check the examples below.
Visit http://api.fanyi.baidu.com/product/113 for more information on how to generate your Baidu appid
and appkey.

- Simple translation

.. code-block:: python
text = 'Hello world'
translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate(text)
- Translate batch of texts

.. code-block:: python
=
texts = ["Hello world", "How are you?"]
translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate_batch(texts)

- Translate from a file:

.. code-block:: python
translated = BaiduTranslator(appid="your-appid", appkey="your-appkey" source="en", target="zh").translate_file('path/to/file')
Proxy usage
-------------
Expand Down
65 changes: 65 additions & 0 deletions tests/test_baidu.py
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)

0 comments on commit 5891228

Please sign in to comment.