File size: 5,507 Bytes
8d368b7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# -*- coding: utf-8 -*-

import requests
# import uuid
import logging
import sys

from deep_translator.constants import BASE_URLS, MICROSOFT_CODES_TO_LANGUAGES
from deep_translator.exceptions import LanguageNotSupportedException, ServerException, MicrosoftAPIerror


class MicrosoftTranslator:
    """
    the class that wraps functions, which use the Microsoft translator under the hood to translate word(s)
    """

    _languages = MICROSOFT_CODES_TO_LANGUAGES
    supported_languages = list(_languages.values())

    def __init__(self, api_key=None, region=None, source=None, target=None, **kwargs):
        """
        @params api_key and target are the required params
        @param api_key: your Microsoft API key
        @param region: your Microsoft Location
        """
        if not api_key:
            raise ServerException(401)
        else:
            self.api_key = api_key
        self.headers = {
            "Ocp-Apim-Subscription-Key": self.api_key,
            "Content-type": "application/json",
        }
        # region is not required but very common and goes to headers if passed
        if region:
            self.region = region
            self.headers["Ocp-Apim-Subscription-Region"] = self.region

        if not target:
            raise ServerException(401)
        else:
            if type(target) is str:
                self.target = target.lower()
            else:
                self.target = [i.lower() for i in target]
            if self.is_language_supported(self.target):
                self.target = self._map_language_to_code(self.target)

        self.url_params = {'to': self.target, **kwargs}

        if source:
            self.source = source.lower()
            if self.is_language_supported(self.source):
                self.source = self._map_language_to_code(self.source)
            self.url_params['from'] = self.source

        self.__base_url = BASE_URLS.get("MICROSOFT_TRANSLATE")

    @staticmethod
    def get_supported_languages(as_dict=False):
        """
        return the languages supported by the microsoft translator
        @param as_dict: if True, the languages will be returned as a dictionary mapping languages to their abbreviations
        @return: list or dict
        """
        return MicrosoftTranslator.supported_languages if not as_dict else MicrosoftTranslator._languages

    def _map_language_to_code(self, language):
        """
        map the language to its corresponding code (abbreviation) if the language was passed by its full name by the user
        @param languages: a string (if 1 lang) or a list (if multiple langs)
        @return: mapped value of the language or raise an exception if the language is not supported
        """
        if type(language) is str:
            language = [language]
        for lang in language:
            if lang in self._languages.values():
                yield lang
            elif lang in self._languages.keys():
                yield self._languages[lang]
            else:
                raise LanguageNotSupportedException(lang)

    def is_language_supported(self, language):
        """
        check if the language is supported by the translator
        @param languages: a string (if 1 lang) or a list (if multiple langs)
        @return: bool or raise an Exception
        """
        if type(language) is str:
            language = [language]
        for lang in language:
            if lang not in self._languages.keys():
                if lang not in self._languages.values():
                    raise LanguageNotSupportedException(lang)
        return True

    def translate(self, text):
        """
        function that uses microsoft translate to translate a text
        @param text: desired text to translate
        @return: str: translated text
        """
        # a body must be a list of dicts to process multiple texts;
        # I have not added multiple text processing here since it is covered by the translate_batch method
        valid_microsoft_json = [{'text': text}]
        try:
            requested = requests.post(self.__base_url, params=self.url_params, headers=self.headers, json=valid_microsoft_json)
        except requests.exceptions.RequestException:
            exc_type, value, traceback = sys.exc_info()
            logging.warning(f"Returned error: {exc_type.__name__}")

        # Where Microsoft API responds with an api error, it returns a dict in response.json()
        if type(requested.json()) is dict:
            error_message = requested.json()['error']
            raise MicrosoftAPIerror(error_message)
        # Where it responds with a translation, its response.json() is a list e.g. [{'translations': [{'text': 'Hello world!', 'to': 'en'}]}]
        elif type(requested.json()) is list:
            all_translations = [i['text'] for i in requested.json()[0]['translations']]
            return "\n".join(all_translations)

    def translate_file(self, path):
        """
        translate from a file
        @param path: path to file
        @return: translated text
        """
        try:
            with open(path) as f:
                text = f.read()
            return self.translate(text)
        except Exception as e:
            raise e

    def translate_batch(self, batch):
        """
        translate a batch of texts
        @param batch: list of texts to translate
        @return: list of translations
        """
        return [self.translate(text) for text in batch]