├── MANIFEST.in ├── requirements.txt ├── setup.cfg ├── test-requirements.txt ├── docs ├── _themes │ └── solar │ │ ├── theme.conf │ │ ├── static │ │ ├── subtle_dots.png │ │ ├── solarized-dark.css │ │ └── solar.css │ │ ├── NEWS.txt │ │ ├── README.rst │ │ └── layout.html ├── index.rst ├── Makefile ├── make.bat └── conf.py ├── tests ├── conftest.py ├── test_utils.py ├── test_gtoken.py └── test_client.py ├── googletrans ├── urls.py ├── __init__.py ├── compat.py ├── adapters.py ├── models.py ├── utils.py ├── constants.py ├── gtoken.py └── client.py ├── .travis.yml ├── Pipfile ├── pytest.ini ├── tox.ini ├── .gitignore ├── LICENSE ├── example └── translate_word_doc.py ├── translate ├── setup.py ├── README.rst └── Pipfile.lock /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.13.0 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.13.0 2 | future==0.14.3 3 | coveralls==1.1 4 | -------------------------------------------------------------------------------- /docs/_themes/solar/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = solar.css 4 | pygments_style = none 5 | -------------------------------------------------------------------------------- /docs/_themes/solar/static/subtle_dots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shivasiddharth/py-googletrans/master/docs/_themes/solar/static/subtle_dots.png -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from pytest import fixture 2 | 3 | 4 | @fixture 5 | def translator(): 6 | from googletrans import Translator 7 | return Translator() -------------------------------------------------------------------------------- /googletrans/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Predefined URLs used to make google translate requests. 4 | """ 5 | BASE = 'https://translate.google.com' 6 | TRANSLATE = 'https://{host}/translate_a/single' 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 2.7 4 | - 3.6 5 | - pypy 6 | install: 7 | - pip install tox-travis 8 | - pip install pytest-cov coveralls 9 | script: tox 10 | after_success: coveralls 11 | -------------------------------------------------------------------------------- /googletrans/__init__.py: -------------------------------------------------------------------------------- 1 | """Free Google Translate API for Python. Translates totally free of charge.""" 2 | __all__ = 'Translator', 3 | __version__ = '2.3.0' 4 | 5 | 6 | from googletrans.client import Translator 7 | from googletrans.constants import LANGCODES, LANGUAGES 8 | -------------------------------------------------------------------------------- /googletrans/compat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | try: # pragma: nocover 4 | from urllib.parse import quote 5 | except: # pragma: nocover 6 | from urllib import quote 7 | 8 | 9 | PY3 = sys.version_info > (3, ) 10 | 11 | unicode = str if PY3 else unicode 12 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.python.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "==2.13.0" 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | [dev-packages] 16 | 17 | future = "*" 18 | coveralls = "*" 19 | "pytest-watch" = "*" 20 | "pytest-testmon" = "*" 21 | 22 | 23 | [requires] 24 | python_version = "3.6" -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = -v 3 | omit = 4 | tests/* 5 | 6 | [run] 7 | include = googletrans/* 8 | 9 | [report] 10 | exclude_lines = 11 | pragma: no cover 12 | def __repr__ 13 | if self.debug: 14 | if settings.DEBUG 15 | raise AssertionError 16 | raise NotImplementedError 17 | if 0: 18 | if __name__ == .__main__.: 19 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27,py36,pypy 3 | 4 | [testenv] 5 | deps= 6 | -r{toxinidir}/requirements.txt 7 | pytest 8 | pytest-capturelog 9 | pytest-cov 10 | commands= 11 | py.test --cov-report= --cov={envsitepackagesdir}/googletrans {posargs:} 12 | 13 | [pytest] 14 | addopts = -v 15 | testpaths = googletrans/ tests/ 16 | 17 | [flake8] 18 | exclude = .tox 19 | -------------------------------------------------------------------------------- /googletrans/adapters.py: -------------------------------------------------------------------------------- 1 | from requests.adapters import HTTPAdapter 2 | 3 | 4 | class TimeoutAdapter(HTTPAdapter): 5 | """HTTP adapter that adds timeout to each query.""" 6 | def __init__(self, timeout=None, *args, **kwargs): 7 | """HTTP adapter that adds timeout to each query. 8 | 9 | :param timeout: Timeout that will be added to each query 10 | """ 11 | self.timeout = timeout 12 | super(TimeoutAdapter, self).__init__(*args, **kwargs) 13 | 14 | def send(self, *args, **kwargs): 15 | kwargs['timeout'] = self.timeout 16 | return super(TimeoutAdapter, self).send(*args, **kwargs) 17 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | from googletrans import utils 2 | from pytest import raises 3 | 4 | 5 | def test_format_json(): 6 | text = '[,,"en",,,,0.96954316,,[["en"],,[0.96954316]]]' 7 | 8 | result = utils.legacy_format_json(text) 9 | 10 | assert result == [None, None, 'en', None, None, None, 0.96954316, None, 11 | [['en'], None, [0.96954316]]] 12 | 13 | def test_format_malformed_json(): 14 | text = '[,,"en",,,,0.96954316,,[["en"],,0.96954316]]]' 15 | 16 | with raises(ValueError): 17 | utils.legacy_format_json(text) 18 | 19 | def test_rshift(): 20 | value, n = 1000, 3 21 | 22 | result = utils.rshift(value, n) 23 | 24 | assert result == 125 25 | -------------------------------------------------------------------------------- /docs/_themes/solar/NEWS.txt: -------------------------------------------------------------------------------- 1 | News 2 | ==== 3 | 4 | 1.3 5 | --- 6 | * Release date: 2012-11-01. 7 | * Source Code Pro is now used for code samples. 8 | * Reduced font size of pre elements. 9 | * Horizontal rule for header elements. 10 | * HTML pre contents are now wrapped (no scrollbars). 11 | * Changed permalink color from black to a lighter one. 12 | 13 | 1.2 14 | --- 15 | * Release date: 2012-10-03. 16 | * Style additional admonition levels. 17 | * Increase padding for navigation links (minor). 18 | * Add shadow for admonition items (minor). 19 | 20 | 1.1 21 | --- 22 | * Release date: 2012-09-05. 23 | * Add a new background. 24 | * Revert font of headings to Open Sans Light. 25 | * Darker color for h3 - h6. 26 | * Removed dependency on solarized dark pygments style. 27 | * Nice looking scrollbars for pre element. 28 | 29 | 1.0 30 | --- 31 | * Release date: 2012-08-24. 32 | * Initial release. 33 | -------------------------------------------------------------------------------- /docs/_themes/solar/README.rst: -------------------------------------------------------------------------------- 1 | Solar theme for Python Sphinx 2 | ============================= 3 | Solar is an attempt to create a theme for Sphinx based on the `Solarized `_ color scheme. 4 | 5 | Preview 6 | ------- 7 | http://vimalkumar.in/sphinx-themes/solar 8 | 9 | Download 10 | -------- 11 | Released versions are available from http://github.com/vkvn/sphinx-themes/downloads 12 | 13 | Installation 14 | ------------ 15 | #. Extract the archive. 16 | #. Modify ``conf.py`` of an existing Sphinx project or create new project using ``sphinx-quickstart``. 17 | #. Change the ``html_theme`` parameter to ``solar``. 18 | #. Change the ``html_theme_path`` to the location containing the extracted archive. 19 | 20 | License 21 | ------- 22 | `GNU General Public License `_. 23 | 24 | Credits 25 | ------- 26 | Modified from the default Sphinx theme -- Sphinxdoc 27 | 28 | Background pattern from http://subtlepatterns.com. 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | .testmondata 46 | 47 | # Translations 48 | *.mo 49 | *.pot 50 | 51 | # Django stuff: 52 | *.log 53 | 54 | # Sphinx documentation 55 | docs/_build/ 56 | 57 | # PyBuilder 58 | target/ 59 | 60 | .DS_Store 61 | .python-version 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 SuHun Han 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. 22 | 23 | -------------------------------------------------------------------------------- /example/translate_word_doc.py: -------------------------------------------------------------------------------- 1 | from docx import Document 2 | from googletrans import Translator 3 | 4 | 5 | def translate_doc(filename, destination='zh-CN', mix=True): 6 | """ 7 | translate a word document type of file and save the result as document and keep the exactly same file format. 8 | :param filename: word doc file 9 | :param destination='zh-CN': 10 | :param mix=True: if True, will have original language and target language into the same doc. paragraphs by paragraphs. 11 | """ 12 | def tx(t): return Translator().translate(t, dest=destination).text 13 | doc = Document(filename) 14 | for p in doc.paragraphs: 15 | txd = tx(p.text) 16 | 17 | p.text = p.text + ('\n' + txd if mix else '') 18 | 19 | for table in doc.tables: 20 | for row in table.rows: 21 | for cell in row.cells: 22 | txd = tx(cell.text) 23 | p.text = cell.text + ('\n' + txd if mix else '') 24 | 25 | f = filename.replace('.doc', destination.lower() + '.doc') 26 | doc.save(f) 27 | 28 | if __name__ == '__main__': 29 | filename = 'p1.docx' 30 | translate_doc(filename) 31 | -------------------------------------------------------------------------------- /tests/test_gtoken.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from googletrans import gtoken 3 | from pytest import fixture 4 | 5 | 6 | @fixture 7 | def acquirer(): 8 | return gtoken.TokenAcquirer() 9 | 10 | 11 | def test_acquire_token(acquirer): 12 | text = 'test' 13 | 14 | result = acquirer.do(text) 15 | 16 | assert result 17 | 18 | 19 | def test_acquire_token_ascii_less_than_2048(acquirer): 20 | text = u'Ѐ' 21 | 22 | result = acquirer.do(text) 23 | 24 | assert result 25 | 26 | 27 | def test_acquire_token_ascii_matches_special_condition(acquirer): 28 | def unichar(i): 29 | try: 30 | return unichr(i) 31 | except NameError: 32 | return chr(i) 33 | text = unichar(55296) + unichar(56320) 34 | 35 | result = acquirer.do(text) 36 | 37 | assert result 38 | 39 | 40 | def test_acquire_token_ascii_else(acquirer): 41 | text = u'가' 42 | 43 | result = acquirer.do(text) 44 | 45 | assert result 46 | 47 | 48 | def test_reuse_valid_token(acquirer): 49 | text = 'test' 50 | 51 | first = acquirer.do(text) 52 | second = acquirer.do(text) 53 | 54 | assert first == second 55 | 56 | 57 | def test_map_lazy_return(acquirer): 58 | value = True 59 | 60 | func = acquirer._lazy(value) 61 | 62 | assert callable(func) 63 | assert func() == value 64 | -------------------------------------------------------------------------------- /translate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import argparse 4 | import sys 5 | from googletrans import Translator 6 | 7 | def main(): 8 | parser = argparse.ArgumentParser( 9 | description='Python Google Translator as a command-line tool') 10 | parser.add_argument('text', help='The text you want to translate.') 11 | parser.add_argument('-d', '--dest', default='en', 12 | help='The destination language you want to translate. (Default: en)') 13 | parser.add_argument('-s', '--src', default='auto', 14 | help='The source language you want to translate. (Default: auto)') 15 | parser.add_argument('-c', '--detect', action='store_true', default=False, 16 | help='') 17 | args = parser.parse_args() 18 | translator = Translator() 19 | 20 | if args.detect: 21 | result = translator.detect(args.text) 22 | result = """ 23 | [{lang}, {confidence}] {text} 24 | """.strip().format(text=args.text, 25 | lang=result.lang, confidence=result.confidence) 26 | print(result) 27 | return 28 | 29 | result = translator.translate(args.text, dest=args.dest, src=args.src) 30 | result = u""" 31 | [{src}] {original} 32 | -> 33 | [{dest}] {text} 34 | [pron.] {pronunciation} 35 | """.strip().format(src=result.src, dest=result.dest, original=result.origin, 36 | text=result.text, pronunciation=result.pronunciation) 37 | print(result) 38 | 39 | if __name__ == '__main__': 40 | main() 41 | -------------------------------------------------------------------------------- /googletrans/models.py: -------------------------------------------------------------------------------- 1 | class Translated(object): 2 | """Translate result object 3 | 4 | :param src: source langauge (default: auto) 5 | :param dest: destination language (default: en) 6 | :param origin: original text 7 | :param text: translated text 8 | :param pronunciation: pronunciation 9 | """ 10 | def __init__(self, src, dest, origin, text, pronunciation, extra_data=None): 11 | self.src = src 12 | self.dest = dest 13 | self.origin = origin 14 | self.text = text 15 | self.pronunciation = pronunciation 16 | self.extra_data = extra_data 17 | 18 | def __str__(self): # pragma: nocover 19 | return self.__unicode__() 20 | 21 | def __unicode__(self): # pragma: nocover 22 | return u'Translated(src={src}, dest={dest}, text={text}, pronunciation={pronunciation}, ' \ 23 | u'extra_data={extra_data})'.format( 24 | src=self.src, dest=self.dest, text=self.text, pronunciation=self.pronunciation, 25 | extra_data='"' + repr(self.extra_data)[:10] + '..."') 26 | 27 | 28 | class Detected(object): 29 | """Language detection result object 30 | 31 | :param lang: detected language 32 | :param confidence: the confidence of detection result (0.00 to 1.00) 33 | """ 34 | def __init__(self, lang, confidence): 35 | self.lang = lang 36 | self.confidence = confidence 37 | 38 | def __str__(self): # pragma: nocover 39 | return self.__unicode__() 40 | 41 | def __unicode__(self): # pragma: nocover 42 | return u'Detected(lang={lang}, confidence={confidence})'.format( 43 | lang=self.lang, confidence=self.confidence) -------------------------------------------------------------------------------- /googletrans/utils.py: -------------------------------------------------------------------------------- 1 | """A conversion module for googletrans""" 2 | from __future__ import print_function 3 | import re 4 | import json 5 | 6 | 7 | def build_params(query, src, dest, token): 8 | params = { 9 | 'client': 't', 10 | 'sl': src, 11 | 'tl': dest, 12 | 'hl': dest, 13 | 'dt': ['at', 'bd', 'ex', 'ld', 'md', 'qca', 'rw', 'rm', 'ss', 't'], 14 | 'ie': 'UTF-8', 15 | 'oe': 'UTF-8', 16 | 'otf': 1, 17 | 'ssel': 0, 18 | 'tsel': 0, 19 | 'tk': token, 20 | 'q': query, 21 | } 22 | return params 23 | 24 | 25 | def legacy_format_json(original): 26 | # save state 27 | states = [] 28 | text = original 29 | 30 | # save position for double-quoted texts 31 | for i, pos in enumerate(re.finditer('"', text)): 32 | # pos.start() is a double-quote 33 | p = pos.start() + 1 34 | if i % 2 == 0: 35 | nxt = text.find('"', p) 36 | states.append((p, text[p:nxt])) 37 | 38 | # replace all weired characters in text 39 | while text.find(',,') > -1: 40 | text = text.replace(',,', ',null,') 41 | while text.find('[,') > -1: 42 | text = text.replace('[,', '[null,') 43 | 44 | # recover state 45 | for i, pos in enumerate(re.finditer('"', text)): 46 | p = pos.start() + 1 47 | if i % 2 == 0: 48 | j = int(i / 2) 49 | nxt = text.find('"', p) 50 | # replacing a portion of a string 51 | # use slicing to extract those parts of the original string to be kept 52 | text = text[:p] + states[j][1] + text[nxt:] 53 | 54 | converted = json.loads(text) 55 | return converted 56 | 57 | 58 | def format_json(original): 59 | try: 60 | converted = json.loads(original) 61 | except ValueError: 62 | converted = legacy_format_json(original) 63 | 64 | return converted 65 | 66 | 67 | def rshift(val, n): 68 | """python port for '>>>'(right shift with padding) 69 | """ 70 | return (val % 0x100000000) >> n 71 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import os.path 4 | import re 5 | 6 | from setuptools import setup, find_packages 7 | 8 | 9 | def get_file(*paths): 10 | path = os.path.join(*paths) 11 | try: 12 | with open(path, 'rb') as f: 13 | return f.read().decode('utf8') 14 | except IOError: 15 | pass 16 | 17 | 18 | def get_version(): 19 | init_py = get_file(os.path.dirname(__file__), 'googletrans', '__init__.py') 20 | pattern = r"{0}\W*=\W*'([^']+)'".format('__version__') 21 | version, = re.findall(pattern, init_py) 22 | return version 23 | 24 | 25 | def get_description(): 26 | init_py = get_file(os.path.dirname(__file__), 'googletrans', '__init__.py') 27 | pattern = r'"""(.*?)"""' 28 | description, = re.findall(pattern, init_py, re.DOTALL) 29 | return description 30 | 31 | 32 | def get_readme(): 33 | return get_file(os.path.dirname(__file__), 'README.rst') 34 | 35 | 36 | def install(): 37 | setup( 38 | name='googletrans', 39 | version=get_version(), 40 | description=get_description(), 41 | long_description=get_readme(), 42 | license='MIT', 43 | author='SuHun Han', 44 | author_email='ssut' '@' 'ssut.me', 45 | url='https://github.com/ssut/py-googletrans', 46 | classifiers=['Development Status :: 5 - Production/Stable', 47 | 'Intended Audience :: Education', 48 | 'Intended Audience :: End Users/Desktop', 49 | 'License :: Freeware', 50 | 'Operating System :: POSIX', 51 | 'Operating System :: Microsoft :: Windows', 52 | 'Operating System :: MacOS :: MacOS X', 53 | 'Topic :: Education', 54 | 'Programming Language :: Python', 55 | 'Programming Language :: Python :: 2.7', 56 | 'Programming Language :: Python :: 3.4', 57 | 'Programming Language :: Python :: 3.5', 58 | 'Programming Language :: Python :: 3.6'], 59 | packages=find_packages(exclude=['docs', 'tests']), 60 | keywords='google translate translator', 61 | install_requires=[ 62 | 'requests', 63 | ], 64 | extras_require={ 65 | 'h2': ['hyper'], 66 | }, 67 | tests_require=[ 68 | 'pytest', 69 | 'coveralls', 70 | ], 71 | scripts=['translate'] 72 | ) 73 | 74 | 75 | if __name__ == "__main__": 76 | install() 77 | -------------------------------------------------------------------------------- /docs/_themes/solar/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | 3 | {%- block doctype -%} 4 | 5 | {%- endblock -%} 6 | 7 | {%- block extrahead -%} 8 | 9 | 10 | {%- endblock -%} 11 | 12 | {# put the sidebar before the body #} 13 | {% block sidebar1 %}{{ sidebar() }}{% endblock %} 14 | {% block sidebar2 %} 15 | 16 | {% endblock %} 17 | 18 | {%- block footer %} 19 | 34 | {%- endblock %} 35 | -------------------------------------------------------------------------------- /googletrans/constants.py: -------------------------------------------------------------------------------- 1 | DEFAULT_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' 2 | 3 | SPECIAL_CASES = { 4 | 'ee': 'et', 5 | } 6 | 7 | LANGUAGES = { 8 | 'af': 'afrikaans', 9 | 'sq': 'albanian', 10 | 'am': 'amharic', 11 | 'ar': 'arabic', 12 | 'hy': 'armenian', 13 | 'az': 'azerbaijani', 14 | 'eu': 'basque', 15 | 'be': 'belarusian', 16 | 'bn': 'bengali', 17 | 'bs': 'bosnian', 18 | 'bg': 'bulgarian', 19 | 'ca': 'catalan', 20 | 'ceb': 'cebuano', 21 | 'ny': 'chichewa', 22 | 'zh-cn': 'chinese (simplified)', 23 | 'zh-tw': 'chinese (traditional)', 24 | 'co': 'corsican', 25 | 'hr': 'croatian', 26 | 'cs': 'czech', 27 | 'da': 'danish', 28 | 'nl': 'dutch', 29 | 'en': 'english', 30 | 'eo': 'esperanto', 31 | 'et': 'estonian', 32 | 'tl': 'filipino', 33 | 'fi': 'finnish', 34 | 'fr': 'french', 35 | 'fy': 'frisian', 36 | 'gl': 'galician', 37 | 'ka': 'georgian', 38 | 'de': 'german', 39 | 'el': 'greek', 40 | 'gu': 'gujarati', 41 | 'ht': 'haitian creole', 42 | 'ha': 'hausa', 43 | 'haw': 'hawaiian', 44 | 'iw': 'hebrew', 45 | 'hi': 'hindi', 46 | 'hmn': 'hmong', 47 | 'hu': 'hungarian', 48 | 'is': 'icelandic', 49 | 'ig': 'igbo', 50 | 'id': 'indonesian', 51 | 'ga': 'irish', 52 | 'it': 'italian', 53 | 'ja': 'japanese', 54 | 'jw': 'javanese', 55 | 'kn': 'kannada', 56 | 'kk': 'kazakh', 57 | 'km': 'khmer', 58 | 'ko': 'korean', 59 | 'ku': 'kurdish (kurmanji)', 60 | 'ky': 'kyrgyz', 61 | 'lo': 'lao', 62 | 'la': 'latin', 63 | 'lv': 'latvian', 64 | 'lt': 'lithuanian', 65 | 'lb': 'luxembourgish', 66 | 'mk': 'macedonian', 67 | 'mg': 'malagasy', 68 | 'ms': 'malay', 69 | 'ml': 'malayalam', 70 | 'mt': 'maltese', 71 | 'mi': 'maori', 72 | 'mr': 'marathi', 73 | 'mn': 'mongolian', 74 | 'my': 'myanmar (burmese)', 75 | 'ne': 'nepali', 76 | 'no': 'norwegian', 77 | 'ps': 'pashto', 78 | 'fa': 'persian', 79 | 'pl': 'polish', 80 | 'pt': 'portuguese', 81 | 'pa': 'punjabi', 82 | 'ro': 'romanian', 83 | 'ru': 'russian', 84 | 'sm': 'samoan', 85 | 'gd': 'scots gaelic', 86 | 'sr': 'serbian', 87 | 'st': 'sesotho', 88 | 'sn': 'shona', 89 | 'sd': 'sindhi', 90 | 'si': 'sinhala', 91 | 'sk': 'slovak', 92 | 'sl': 'slovenian', 93 | 'so': 'somali', 94 | 'es': 'spanish', 95 | 'su': 'sundanese', 96 | 'sw': 'swahili', 97 | 'sv': 'swedish', 98 | 'tg': 'tajik', 99 | 'ta': 'tamil', 100 | 'te': 'telugu', 101 | 'th': 'thai', 102 | 'tr': 'turkish', 103 | 'uk': 'ukrainian', 104 | 'ur': 'urdu', 105 | 'uz': 'uzbek', 106 | 'vi': 'vietnamese', 107 | 'cy': 'welsh', 108 | 'xh': 'xhosa', 109 | 'yi': 'yiddish', 110 | 'yo': 'yoruba', 111 | 'zu': 'zulu', 112 | 'fil': 'Filipino', 113 | 'he': 'Hebrew' 114 | } 115 | 116 | LANGCODES = dict(map(reversed, LANGUAGES.items())) 117 | -------------------------------------------------------------------------------- /tests/test_client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from pytest import raises 3 | from requests.exceptions import ConnectionError 4 | from requests.exceptions import ReadTimeout 5 | 6 | from googletrans import Translator 7 | 8 | 9 | def test_bind_multiple_service_urls(): 10 | service_urls = [ 11 | 'translate.google.com', 12 | 'translate.google.co.kr', 13 | ] 14 | 15 | translator = Translator(service_urls=service_urls) 16 | assert translator.service_urls == service_urls 17 | 18 | assert translator.translate('test', dest='ko') 19 | assert translator.detect('Hello') 20 | 21 | 22 | def test_source_language(translator): 23 | result = translator.translate('안녕하세요.') 24 | assert result.src == 'ko' 25 | 26 | 27 | def test_pronunciation(translator): 28 | result = translator.translate('안녕하세요.', dest='ja') 29 | assert result.pronunciation == 'Kon\'nichiwa.' 30 | 31 | 32 | def test_latin_to_english(translator): 33 | result = translator.translate('veritas lux mea', src='la', dest='en') 34 | assert result.text == 'The truth is my light' 35 | 36 | 37 | def test_unicode(translator): 38 | result = translator.translate(u'안녕하세요.', src='ko', dest='ja') 39 | assert result.text == u'こんにちは。' 40 | 41 | 42 | def test_language_name(translator): 43 | result = translator.translate(u'Hello', src='ENGLISH', dest='iRiSh') 44 | assert result.text == u'Dia dhuit' 45 | 46 | 47 | def test_language_name_with_space(translator): 48 | result = translator.translate(u'Hello', src='en', dest='chinese (simplified)') 49 | assert result.dest == 'zh-cn' 50 | 51 | 52 | def test_language_rfc1766(translator): 53 | result = translator.translate(u'luna', src='it_ch@euro', dest='en') 54 | assert result.text == u'moon' 55 | 56 | 57 | def test_special_chars(translator): 58 | text = u"©×《》" 59 | 60 | result = translator.translate(text, src='en', dest='en') 61 | assert result.text == text 62 | 63 | 64 | def test_translate_list(translator): 65 | args = (['test', 'exam'], 'ko', 'en') 66 | translations = translator.translate(*args) 67 | 68 | assert translations[0].text == u'테스트' 69 | assert translations[1].text == u'시험' 70 | 71 | 72 | def test_detect_language(translator): 73 | ko = translator.detect(u'한국어') 74 | en = translator.detect('English') 75 | 76 | assert ko.lang == 'ko' 77 | assert en.lang == 'en' 78 | 79 | 80 | def test_detect_list(translator): 81 | items = [u'한국어', ' English'] 82 | 83 | result = translator.detect(items) 84 | 85 | assert result[0].lang == 'ko' 86 | assert result[1].lang == 'en' 87 | 88 | 89 | def test_src_in_special_cases(translator): 90 | args = ('Tere', 'en', 'ee') 91 | 92 | result = translator.translate(*args) 93 | 94 | assert result.text == 'Hello' 95 | 96 | 97 | def test_src_not_in_supported_languages(translator): 98 | args = ('Hello', 'en', 'zzz') 99 | 100 | with raises(ValueError): 101 | translator.translate(*args) 102 | 103 | 104 | def test_dest_in_special_cases(translator): 105 | args = ('hello', 'ee', 'en') 106 | 107 | result = translator.translate(*args) 108 | 109 | assert result.text == 'Tere' 110 | 111 | 112 | def test_dest_not_in_supported_languages(translator): 113 | args = ('Hello', 'zzz', 'en') 114 | 115 | with raises(ValueError): 116 | translator.translate(*args) 117 | 118 | 119 | def test_connection_timeout(): 120 | # Requests library specifies two timeouts: connection and read 121 | 122 | with raises((ConnectionError, ReadTimeout)): 123 | """If a number is passed to timeout parameter, both connection 124 | and read timeouts will be set to it. 125 | Firstly, the connection timeout will fail. 126 | """ 127 | translator = Translator(timeout=0.00001) 128 | translator.translate('안녕하세요.') 129 | 130 | 131 | def test_read_timeout(): 132 | 133 | with raises(ReadTimeout): 134 | translator = Translator(timeout=(10, 0.00001)) 135 | translator.translate('안녕하세요.') 136 | -------------------------------------------------------------------------------- /docs/_themes/solar/static/solarized-dark.css: -------------------------------------------------------------------------------- 1 | /* solarized dark style for solar theme */ 2 | 3 | /*style pre scrollbar*/ 4 | pre::-webkit-scrollbar, .highlight::-webkit-scrollbar { 5 | height: 0.5em; 6 | background: #073642; 7 | } 8 | 9 | pre::-webkit-scrollbar-thumb { 10 | border-radius: 1em; 11 | background: #93a1a1; 12 | } 13 | 14 | /* pygments style */ 15 | .highlight .hll { background-color: #ffffcc } 16 | .highlight { background: #002B36!important; color: #93A1A1 } 17 | .highlight .c { color: #586E75 } /* Comment */ 18 | .highlight .err { color: #93A1A1 } /* Error */ 19 | .highlight .g { color: #93A1A1 } /* Generic */ 20 | .highlight .k { color: #859900 } /* Keyword */ 21 | .highlight .l { color: #93A1A1 } /* Literal */ 22 | .highlight .n { color: #93A1A1 } /* Name */ 23 | .highlight .o { color: #859900 } /* Operator */ 24 | .highlight .x { color: #CB4B16 } /* Other */ 25 | .highlight .p { color: #93A1A1 } /* Punctuation */ 26 | .highlight .cm { color: #586E75 } /* Comment.Multiline */ 27 | .highlight .cp { color: #859900 } /* Comment.Preproc */ 28 | .highlight .c1 { color: #586E75 } /* Comment.Single */ 29 | .highlight .cs { color: #859900 } /* Comment.Special */ 30 | .highlight .gd { color: #2AA198 } /* Generic.Deleted */ 31 | .highlight .ge { color: #93A1A1; font-style: italic } /* Generic.Emph */ 32 | .highlight .gr { color: #DC322F } /* Generic.Error */ 33 | .highlight .gh { color: #CB4B16 } /* Generic.Heading */ 34 | .highlight .gi { color: #859900 } /* Generic.Inserted */ 35 | .highlight .go { color: #93A1A1 } /* Generic.Output */ 36 | .highlight .gp { color: #93A1A1 } /* Generic.Prompt */ 37 | .highlight .gs { color: #93A1A1; font-weight: bold } /* Generic.Strong */ 38 | .highlight .gu { color: #CB4B16 } /* Generic.Subheading */ 39 | .highlight .gt { color: #93A1A1 } /* Generic.Traceback */ 40 | .highlight .kc { color: #CB4B16 } /* Keyword.Constant */ 41 | .highlight .kd { color: #268BD2 } /* Keyword.Declaration */ 42 | .highlight .kn { color: #859900 } /* Keyword.Namespace */ 43 | .highlight .kp { color: #859900 } /* Keyword.Pseudo */ 44 | .highlight .kr { color: #268BD2 } /* Keyword.Reserved */ 45 | .highlight .kt { color: #DC322F } /* Keyword.Type */ 46 | .highlight .ld { color: #93A1A1 } /* Literal.Date */ 47 | .highlight .m { color: #2AA198 } /* Literal.Number */ 48 | .highlight .s { color: #2AA198 } /* Literal.String */ 49 | .highlight .na { color: #93A1A1 } /* Name.Attribute */ 50 | .highlight .nb { color: #B58900 } /* Name.Builtin */ 51 | .highlight .nc { color: #268BD2 } /* Name.Class */ 52 | .highlight .no { color: #CB4B16 } /* Name.Constant */ 53 | .highlight .nd { color: #268BD2 } /* Name.Decorator */ 54 | .highlight .ni { color: #CB4B16 } /* Name.Entity */ 55 | .highlight .ne { color: #CB4B16 } /* Name.Exception */ 56 | .highlight .nf { color: #268BD2 } /* Name.Function */ 57 | .highlight .nl { color: #93A1A1 } /* Name.Label */ 58 | .highlight .nn { color: #93A1A1 } /* Name.Namespace */ 59 | .highlight .nx { color: #93A1A1 } /* Name.Other */ 60 | .highlight .py { color: #93A1A1 } /* Name.Property */ 61 | .highlight .nt { color: #268BD2 } /* Name.Tag */ 62 | .highlight .nv { color: #268BD2 } /* Name.Variable */ 63 | .highlight .ow { color: #859900 } /* Operator.Word */ 64 | .highlight .w { color: #93A1A1 } /* Text.Whitespace */ 65 | .highlight .mf { color: #2AA198 } /* Literal.Number.Float */ 66 | .highlight .mh { color: #2AA198 } /* Literal.Number.Hex */ 67 | .highlight .mi { color: #2AA198 } /* Literal.Number.Integer */ 68 | .highlight .mo { color: #2AA198 } /* Literal.Number.Oct */ 69 | .highlight .sb { color: #586E75 } /* Literal.String.Backtick */ 70 | .highlight .sc { color: #2AA198 } /* Literal.String.Char */ 71 | .highlight .sd { color: #93A1A1 } /* Literal.String.Doc */ 72 | .highlight .s2 { color: #2AA198 } /* Literal.String.Double */ 73 | .highlight .se { color: #CB4B16 } /* Literal.String.Escape */ 74 | .highlight .sh { color: #93A1A1 } /* Literal.String.Heredoc */ 75 | .highlight .si { color: #2AA198 } /* Literal.String.Interpol */ 76 | .highlight .sx { color: #2AA198 } /* Literal.String.Other */ 77 | .highlight .sr { color: #DC322F } /* Literal.String.Regex */ 78 | .highlight .s1 { color: #2AA198 } /* Literal.String.Single */ 79 | .highlight .ss { color: #2AA198 } /* Literal.String.Symbol */ 80 | .highlight .bp { color: #268BD2 } /* Name.Builtin.Pseudo */ 81 | .highlight .vc { color: #268BD2 } /* Name.Variable.Class */ 82 | .highlight .vg { color: #268BD2 } /* Name.Variable.Global */ 83 | .highlight .vi { color: #268BD2 } /* Name.Variable.Instance */ 84 | .highlight .il { color: #2AA198 } /* Literal.Number.Integer.Long */ 85 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | =============================================================== 2 | Googletrans: Free and Unlimited Google translate API for Python 3 | =============================================================== 4 | 5 | .. image:: https://img.shields.io/github/license/mashape/apistatus.svg 6 | :target: http://opensource.org/licenses/MIT 7 | .. image:: https://travis-ci.org/ssut/py-googletrans.svg?branch=master 8 | :target: https://travis-ci.org/ssut/py-googletrans 9 | .. image:: https://readthedocs.org/projects/py-googletrans/badge/?version=latest 10 | :target: https://readthedocs.org/projects/py-googletrans/?badge=latest 11 | .. image:: https://badge.fury.io/py/googletrans.svg 12 | :target: http://badge.fury.io/py/googletrans 13 | .. image:: https://coveralls.io/repos/github/ssut/py-googletrans/badge.svg 14 | :target: https://coveralls.io/github/ssut/py-googletrans 15 | .. image:: https://codeclimate.com/github/ssut/py-googletrans/badges/gpa.svg 16 | :target: https://codeclimate.com/github/ssut/py-googletrans 17 | 18 | Googletrans is a **free** and **unlimited** python library that 19 | implemented Google Translate API. This uses the `Google Translate Ajax 20 | API `__ to make calls to such methods as 21 | detect and translate. 22 | 23 | -------- 24 | Features 25 | -------- 26 | 27 | - Fast and reliable - it uses the same servers that 28 | translate.google.com uses 29 | - Auto language detection 30 | - Bulk translations 31 | - Customizable service URL 32 | - Connection pooling (the advantage of using requests.Session) 33 | - HTTP/2 support 34 | 35 | ~~~~~~~~~~~~~~~~~~~~~ 36 | Note on library usage 37 | ~~~~~~~~~~~~~~~~~~~~~ 38 | 39 | - The maximum character limit on a single text is 15k. 40 | 41 | - Due to limitations of the web version of google translate, this API 42 | does not guarantee that the library would work properly at all times. 43 | (so please use this library if you don't care about stability.) 44 | 45 | - If you want to use a stable API, I highly recommend you to use 46 | `Google's official translate 47 | API `__. 48 | 49 | - If you get HTTP 5xx error or errors like #6, it's probably because 50 | Google has banned your client IP address. 51 | 52 | ---------- 53 | Quickstart 54 | ---------- 55 | 56 | You can install it from PyPI_: 57 | 58 | .. sourcecode:: bash 59 | 60 | $ pip install googletrans 61 | 62 | .. _PyPI: https://pypi.python.org/pypi/googletrans 63 | 64 | ~~~~~~~~~~~~~~ 65 | HTTP/2 support 66 | ~~~~~~~~~~~~~~ 67 | 68 | This is a great deal for everyone! (up to 2x times faster in my test) If 69 | you want to get googletrans faster you should install 70 | `hyper `__ package. Googletrans will 71 | automatically detect if hyper is installed and if so, it will be used 72 | for http networking. 73 | 74 | ~~~~~~~~~~~ 75 | Basic Usage 76 | ~~~~~~~~~~~ 77 | 78 | If source language is not given, google translate attempts to detect the 79 | source language. 80 | 81 | .. code-block:: python 82 | 83 | >>> from googletrans import Translator 84 | >>> translator = Translator() 85 | >>> translator.translate('안녕하세요.') 86 | # 87 | 88 | >>> translator.translate('안녕하세요.', dest='ja') 89 | # 90 | 91 | >>> translator.translate('veritas lux mea', src='la') 92 | # 93 | 94 | ~~~~~~~~~~~~~~~~~~~~~ 95 | Customize service URL 96 | ~~~~~~~~~~~~~~~~~~~~~ 97 | 98 | You can use another google translate domain for translation. If multiple 99 | URLs are provided it then randomly chooses a domain. 100 | 101 | .. code:: python 102 | 103 | >>> from googletrans import Translator 104 | >>> translator = Translator(service_urls=[ 105 | 'translate.google.com', 106 | 'translate.google.co.kr', 107 | ]) 108 | 109 | ~~~~~~~~~~~~~~~~~~~~~ 110 | Advanced Usage (Bulk) 111 | ~~~~~~~~~~~~~~~~~~~~~ 112 | 113 | Array can be used to translate a batch of strings in a single method 114 | call and a single HTTP session. The exact same method shown above work 115 | for arrays as well. 116 | 117 | .. code:: python 118 | 119 | >>> translations = translator.translate(['The quick brown fox', 'jumps over', 'the lazy dog'], dest='ko') 120 | >>> for translation in translations: 121 | ... print(translation.origin, ' -> ', translation.text) 122 | # The quick brown fox -> 빠른 갈색 여우 123 | # jumps over -> 이상 점프 124 | # the lazy dog -> 게으른 개 125 | 126 | ~~~~~~~~~~~~~~~~~~ 127 | Language detection 128 | ~~~~~~~~~~~~~~~~~~ 129 | 130 | The detect method, as its name implies, identifies the language used in 131 | a given sentence. 132 | 133 | .. code:: python 134 | 135 | >>> translator.detect('이 문장은 한글로 쓰여졌습니다.') 136 | # 137 | >>> translator.detect('この文章は日本語で書かれました。') 138 | # 139 | >>> translator.detect('This sentence is written in English.') 140 | # 141 | >>> translator.detect('Tiu frazo estas skribita en Esperanto.') 142 | # 143 | 144 | --------- 145 | API Guide 146 | --------- 147 | 148 | 149 | ====================== 150 | googletrans.Translator 151 | ====================== 152 | 153 | .. autoclass:: googletrans.Translator 154 | :members: 155 | 156 | ================== 157 | googletrans.models 158 | ================== 159 | 160 | .. automodule:: googletrans.models 161 | :members: 162 | 163 | ================== 164 | googletrans.gtoken 165 | ================== 166 | 167 | .. hint:: 168 | 169 | This is for internal use only to generate a valid token to access 170 | translate.google.com ajax API. 171 | 172 | .. automodule:: googletrans.gtoken 173 | :members: 174 | 175 | ===================== 176 | googletrans.LANGUAGES 177 | ===================== 178 | 179 | .. hint:: 180 | 181 | iso639-1 language codes for 182 | `supported languages `_ 183 | for translation. Some language codes also include a country code, like zh-CN or zh-TW. 184 | 185 | .. literalinclude:: ../googletrans/constants.py 186 | :language: python 187 | :lines: 7- 188 | :linenos: -------------------------------------------------------------------------------- /googletrans/gtoken.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import ast 3 | import math 4 | import re 5 | import time 6 | 7 | import requests 8 | 9 | 10 | from googletrans.compat import PY3 11 | from googletrans.compat import unicode 12 | from googletrans.utils import rshift 13 | 14 | 15 | class TokenAcquirer(object): 16 | """Google Translate API token generator 17 | 18 | translate.google.com uses a token to authorize the requests. If you are 19 | not Google, you do have this token and will have to pay for use. 20 | This class is the result of reverse engineering on the obfuscated and 21 | minified code used by Google to generate such token. 22 | 23 | The token is based on a seed which is updated once per hour and on the 24 | text that will be translated. 25 | Both are combined - by some strange math - in order to generate a final 26 | token (e.g. 744915.856682) which is used by the API to validate the 27 | request. 28 | 29 | This operation will cause an additional request to get an initial 30 | token from translate.google.com. 31 | 32 | Example usage: 33 | >>> from googletrans.gtoken import TokenAcquirer 34 | >>> acquirer = TokenAcquirer() 35 | >>> text = 'test' 36 | >>> tk = acquirer.do(text) 37 | >>> tk 38 | 950629.577246 39 | """ 40 | 41 | RE_TKK = re.compile(r'tkk:\'(.+?)\'', re.DOTALL) 42 | RE_RAWTKK = re.compile(r'tkk:\'(.+?)\'', re.DOTALL) 43 | 44 | def __init__(self, tkk='0', session=None, host='translate.google.com'): 45 | self.session = session or requests.Session() 46 | self.tkk = tkk 47 | self.host = host if 'http' in host else 'https://' + host 48 | 49 | def _update(self): 50 | """update tkk 51 | """ 52 | # we don't need to update the base TKK value when it is still valid 53 | now = math.floor(int(time.time() * 1000) / 3600000.0) 54 | if self.tkk and int(self.tkk.split('.')[0]) == now: 55 | return 56 | 57 | r = self.session.get(self.host) 58 | 59 | raw_tkk = self.RE_RAWTKK.search(r.text) 60 | if raw_tkk: 61 | self.tkk = raw_tkk.group(1) 62 | return 63 | 64 | # this will be the same as python code after stripping out a reserved word 'var' 65 | code = unicode(self.RE_TKK.search(r.text).group(1)).replace('var ', '') 66 | # unescape special ascii characters such like a \x3d(=) 67 | if PY3: # pragma: no cover 68 | code = code.encode().decode('unicode-escape') 69 | else: # pragma: no cover 70 | code = code.decode('string_escape') 71 | 72 | if code: 73 | tree = ast.parse(code) 74 | visit_return = False 75 | operator = '+' 76 | n, keys = 0, dict(a=0, b=0) 77 | for node in ast.walk(tree): 78 | if isinstance(node, ast.Assign): 79 | name = node.targets[0].id 80 | if name in keys: 81 | if isinstance(node.value, ast.Num): 82 | keys[name] = node.value.n 83 | # the value can sometimes be negative 84 | elif isinstance(node.value, ast.UnaryOp) and \ 85 | isinstance(node.value.op, ast.USub): # pragma: nocover 86 | keys[name] = -node.value.operand.n 87 | elif isinstance(node, ast.Return): 88 | # parameters should be set after this point 89 | visit_return = True 90 | elif visit_return and isinstance(node, ast.Num): 91 | n = node.n 92 | elif visit_return and n > 0: 93 | # the default operator is '+' but implement some more for 94 | # all possible scenarios 95 | if isinstance(node, ast.Add): # pragma: nocover 96 | pass 97 | elif isinstance(node, ast.Sub): # pragma: nocover 98 | operator = '-' 99 | elif isinstance(node, ast.Mult): # pragma: nocover 100 | operator = '*' 101 | elif isinstance(node, ast.Pow): # pragma: nocover 102 | operator = '**' 103 | elif isinstance(node, ast.BitXor): # pragma: nocover 104 | operator = '^' 105 | # a safety way to avoid Exceptions 106 | clause = compile('{1}{0}{2}'.format( 107 | operator, keys['a'], keys['b']), '', 'eval') 108 | value = eval(clause, dict(__builtin__={})) 109 | result = '{}.{}'.format(n, value) 110 | 111 | self.tkk = result 112 | 113 | def _lazy(self, value): 114 | """like lazy evalution, this method returns a lambda function that 115 | returns value given. 116 | We won't be needing this because this seems to have been built for 117 | code obfuscation. 118 | 119 | the original code of this method is as follows: 120 | 121 | ... code-block: javascript 122 | 123 | var ek = function(a) { 124 | return function() { 125 | return a; 126 | }; 127 | } 128 | """ 129 | return lambda: value 130 | 131 | def _xr(self, a, b): 132 | size_b = len(b) 133 | c = 0 134 | while c < size_b - 2: 135 | d = b[c + 2] 136 | d = ord(d[0]) - 87 if 'a' <= d else int(d) 137 | d = rshift(a, d) if '+' == b[c + 1] else a << d 138 | a = a + d & 4294967295 if '+' == b[c] else a ^ d 139 | 140 | c += 3 141 | return a 142 | 143 | def acquire(self, text): 144 | b = self.tkk if self.tkk != '0' else '' 145 | d = b.split('.') 146 | b = int(d[0]) if len(d) > 1 else 0 147 | 148 | # assume e means char code array 149 | e = [] 150 | g = 0 151 | size = len(text) 152 | for i, char in enumerate(text): 153 | l = ord(char) 154 | # just append if l is less than 128(ascii: DEL) 155 | if l < 128: 156 | e.append(l) 157 | # append calculated value if l is less than 2048 158 | else: 159 | if l < 2048: 160 | e.append(l >> 6 | 192) 161 | else: 162 | # append calculated value if l matches special condition 163 | if (l & 64512) == 55296 and g + 1 < size and \ 164 | ord(text[g + 1]) & 64512 == 56320: 165 | g += 1 166 | l = 65536 + ((l & 1023) << 10) + ord(text[g]) & 1023 167 | e.append(l >> 18 | 240) 168 | e.append(l >> 12 & 63 | 128) 169 | else: 170 | e.append(l >> 12 | 224) 171 | e.append(l >> 6 & 63 | 128) 172 | e.append(l & 63 | 128) 173 | a = b 174 | for i, value in enumerate(e): 175 | a += value 176 | a = self._xr(a, '+-a^+6') 177 | a = self._xr(a, '+-3^+b+-f') 178 | a ^= int(d[1]) if len(d) > 1 else 0 179 | if a < 0: # pragma: nocover 180 | a = (a & 2147483647) + 2147483648 181 | a %= 1000000 # int(1E6) 182 | 183 | return '{}.{}'.format(a, a ^ b) 184 | 185 | def do(self, text): 186 | self._update() 187 | tk = self.acquire(text) 188 | return tk 189 | -------------------------------------------------------------------------------- /docs/_themes/solar/static/solar.css: -------------------------------------------------------------------------------- 1 | /* solar.css 2 | * Modified from sphinxdoc.css of the sphinxdoc theme. 3 | */ 4 | 5 | @import url("basic.css"); 6 | 7 | /* -- page layout ----------------------------------------------------------- */ 8 | 9 | body { 10 | font-family: 'Open Sans', sans-serif; 11 | font-size: 14px; 12 | line-height: 150%; 13 | text-align: center; 14 | color: #002b36; 15 | padding: 0; 16 | margin: 0px 80px 0px 80px; 17 | min-width: 740px; 18 | -moz-box-shadow: 0px 0px 10px #93a1a1; 19 | -webkit-box-shadow: 0px 0px 10px #93a1a1; 20 | box-shadow: 0px 0px 10px #93a1a1; 21 | background: url("subtle_dots.png") repeat; 22 | 23 | } 24 | 25 | div.document { 26 | background-color: #fcfcfc; 27 | text-align: left; 28 | background-repeat: repeat-x; 29 | } 30 | 31 | div.bodywrapper { 32 | margin: 0 240px 0 0; 33 | border-right: 1px dotted #eee8d5; 34 | } 35 | 36 | div.body { 37 | background-color: white; 38 | margin: 0; 39 | padding: 0.5em 20px 20px 20px; 40 | } 41 | 42 | div.related { 43 | font-size: 1em; 44 | background: #002b36; 45 | color: #839496; 46 | padding: 5px 0px; 47 | } 48 | 49 | div.related ul { 50 | height: 2em; 51 | margin: 2px; 52 | } 53 | 54 | div.related ul li { 55 | margin: 0; 56 | padding: 0; 57 | height: 2em; 58 | float: left; 59 | } 60 | 61 | div.related ul li.right { 62 | float: right; 63 | margin-right: 5px; 64 | } 65 | 66 | div.related ul li a { 67 | margin: 0; 68 | padding: 2px 5px; 69 | line-height: 2em; 70 | text-decoration: none; 71 | color: #839496; 72 | } 73 | 74 | div.related ul li a:hover { 75 | background-color: #073642; 76 | -webkit-border-radius: 2px; 77 | -moz-border-radius: 2px; 78 | border-radius: 2px; 79 | } 80 | 81 | div.sphinxsidebarwrapper { 82 | padding: 0; 83 | } 84 | 85 | div.sphinxsidebar { 86 | margin: 0; 87 | padding: 0.5em 15px 15px 0; 88 | width: 210px; 89 | float: right; 90 | font-size: 0.9em; 91 | text-align: left; 92 | } 93 | 94 | div.sphinxsidebar h3, div.sphinxsidebar h4 { 95 | margin: 1em 0 0.5em 0; 96 | font-size: 1em; 97 | padding: 0.7em; 98 | background-color: #eeeff1; 99 | } 100 | 101 | div.sphinxsidebar h3 a { 102 | color: #2E3436; 103 | } 104 | 105 | div.sphinxsidebar ul { 106 | padding-left: 1.5em; 107 | margin-top: 7px; 108 | padding: 0; 109 | line-height: 150%; 110 | color: #586e75; 111 | } 112 | 113 | div.sphinxsidebar ul ul { 114 | margin-left: 20px; 115 | } 116 | 117 | div.sphinxsidebar input { 118 | border: 1px solid #eee8d5; 119 | } 120 | 121 | div.footer { 122 | background-color: #93a1a1; 123 | color: #eee; 124 | padding: 3px 8px 3px 0; 125 | clear: both; 126 | font-size: 0.8em; 127 | text-align: right; 128 | } 129 | 130 | div.footer a { 131 | color: #eee; 132 | text-decoration: none; 133 | } 134 | 135 | /* -- body styles ----------------------------------------------------------- */ 136 | 137 | p { 138 | margin: 0.8em 0 0.5em 0; 139 | } 140 | 141 | div.body a, div.sphinxsidebarwrapper a { 142 | color: #268bd2; 143 | text-decoration: none; 144 | } 145 | 146 | div.body a:hover, div.sphinxsidebarwrapper a:hover { 147 | border-bottom: 1px solid #268bd2; 148 | } 149 | 150 | h1, h2, h3, h4, h5, h6 { 151 | font-family: "Open Sans", sans-serif; 152 | font-weight: 300; 153 | } 154 | 155 | h1 { 156 | margin: 0; 157 | padding: 0.7em 0 0.3em 0; 158 | line-height: 1.2em; 159 | color: #002b36; 160 | text-shadow: #eee 0.1em 0.1em 0.1em; 161 | } 162 | 163 | h2 { 164 | margin: 1.3em 0 0.2em 0; 165 | padding: 0 0 10px 0; 166 | color: #073642; 167 | border-bottom: 1px solid #eee; 168 | } 169 | 170 | h3 { 171 | margin: 1em 0 -0.3em 0; 172 | padding-bottom: 5px; 173 | } 174 | 175 | h3, h4, h5, h6 { 176 | color: #073642; 177 | border-bottom: 1px dotted #eee; 178 | } 179 | 180 | div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a { 181 | color: #657B83!important; 182 | } 183 | 184 | h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { 185 | display: none; 186 | margin: 0 0 0 0.3em; 187 | padding: 0 0.2em 0 0.2em; 188 | color: #aaa!important; 189 | } 190 | 191 | h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, 192 | h5:hover a.anchor, h6:hover a.anchor { 193 | display: inline; 194 | } 195 | 196 | h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, 197 | h5 a.anchor:hover, h6 a.anchor:hover { 198 | color: #777; 199 | background-color: #eee; 200 | } 201 | 202 | a.headerlink { 203 | color: #c60f0f!important; 204 | font-size: 1em; 205 | margin-left: 6px; 206 | padding: 0 4px 0 4px; 207 | text-decoration: none!important; 208 | } 209 | 210 | a.headerlink:hover { 211 | background-color: #ccc; 212 | color: white!important; 213 | } 214 | 215 | 216 | cite, code, tt { 217 | font-family: 'Source Code Pro', monospace; 218 | font-size: 0.9em; 219 | letter-spacing: 0.01em; 220 | background-color: #eeeff2; 221 | font-style: normal; 222 | } 223 | 224 | hr { 225 | border: 1px solid #eee; 226 | margin: 2em; 227 | } 228 | 229 | .highlight { 230 | -webkit-border-radius: 2px; 231 | -moz-border-radius: 2px; 232 | border-radius: 2px; 233 | } 234 | 235 | pre { 236 | font-family: 'Source Code Pro', monospace; 237 | font-style: normal; 238 | font-size: 0.9em; 239 | letter-spacing: 0.015em; 240 | line-height: 120%; 241 | padding: 0.7em; 242 | white-space: pre-wrap; /* css-3 */ 243 | white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ 244 | white-space: -pre-wrap; /* Opera 4-6 */ 245 | white-space: -o-pre-wrap; /* Opera 7 */ 246 | word-wrap: break-word; /* Internet Explorer 5.5+ */ 247 | } 248 | 249 | pre a { 250 | color: inherit; 251 | text-decoration: underline; 252 | } 253 | 254 | td.linenos pre { 255 | padding: 0.5em 0; 256 | } 257 | 258 | div.quotebar { 259 | background-color: #f8f8f8; 260 | max-width: 250px; 261 | float: right; 262 | padding: 2px 7px; 263 | border: 1px solid #ccc; 264 | } 265 | 266 | div.topic { 267 | background-color: #f8f8f8; 268 | } 269 | 270 | table { 271 | border-collapse: collapse; 272 | margin: 0 -0.5em 0 -0.5em; 273 | } 274 | 275 | table td, table th { 276 | padding: 0.2em 0.5em 0.2em 0.5em; 277 | } 278 | 279 | div.admonition { 280 | font-size: 0.9em; 281 | margin: 1em 0 1em 0; 282 | border: 1px solid #eee; 283 | background-color: #f7f7f7; 284 | padding: 0; 285 | -moz-box-shadow: 0px 8px 6px -8px #93a1a1; 286 | -webkit-box-shadow: 0px 8px 6px -8px #93a1a1; 287 | box-shadow: 0px 8px 6px -8px #93a1a1; 288 | } 289 | 290 | div.admonition p { 291 | margin: 0.5em 1em 0.5em 1em; 292 | padding: 0.2em; 293 | } 294 | 295 | div.admonition pre { 296 | margin: 0.4em 1em 0.4em 1em; 297 | } 298 | 299 | div.admonition p.admonition-title 300 | { 301 | margin: 0; 302 | padding: 0.2em 0 0.2em 0.6em; 303 | color: white; 304 | border-bottom: 1px solid #eee8d5; 305 | font-weight: bold; 306 | background-color: #268bd2; 307 | } 308 | 309 | div.warning p.admonition-title, 310 | div.important p.admonition-title { 311 | background-color: #cb4b16; 312 | } 313 | 314 | div.hint p.admonition-title, 315 | div.tip p.admonition-title { 316 | background-color: #859900; 317 | } 318 | 319 | div.caution p.admonition-title, 320 | div.attention p.admonition-title, 321 | div.danger p.admonition-title, 322 | div.error p.admonition-title { 323 | background-color: #dc322f; 324 | } 325 | 326 | div.admonition ul, div.admonition ol { 327 | margin: 0.1em 0.5em 0.5em 3em; 328 | padding: 0; 329 | } 330 | 331 | div.versioninfo { 332 | margin: 1em 0 0 0; 333 | border: 1px solid #eee; 334 | background-color: #DDEAF0; 335 | padding: 8px; 336 | line-height: 1.3em; 337 | font-size: 0.9em; 338 | } 339 | 340 | div.viewcode-block:target { 341 | background-color: #f4debf; 342 | border-top: 1px solid #eee; 343 | border-bottom: 1px solid #eee; 344 | } 345 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | html: 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 61 | @echo 62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 63 | 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | pickle: 70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 71 | @echo 72 | @echo "Build finished; now you can process the pickle files." 73 | 74 | json: 75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 76 | @echo 77 | @echo "Build finished; now you can process the JSON files." 78 | 79 | htmlhelp: 80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 81 | @echo 82 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 83 | ".hhp project file in $(BUILDDIR)/htmlhelp." 84 | 85 | qthelp: 86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 87 | @echo 88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/py-googletrans.qhcp" 91 | @echo "To view the help file:" 92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/py-googletrans.qhc" 93 | 94 | applehelp: 95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 96 | @echo 97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 98 | @echo "N.B. You won't be able to view it unless you put it in" \ 99 | "~/Library/Documentation/Help or install it in your application" \ 100 | "bundle." 101 | 102 | devhelp: 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/py-googletrans" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/py-googletrans" 109 | @echo "# devhelp" 110 | 111 | epub: 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | latex: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo 119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 121 | "(use \`make latexpdf' here to do that automatically)." 122 | 123 | latexpdf: 124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 125 | @echo "Running LaTeX files through pdflatex..." 126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 128 | 129 | latexpdfja: 130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 131 | @echo "Running LaTeX files through platex and dvipdfmx..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | text: 136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 137 | @echo 138 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 139 | 140 | man: 141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 142 | @echo 143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 144 | 145 | texinfo: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo 148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 149 | @echo "Run \`make' in that directory to run these through makeinfo" \ 150 | "(use \`make info' here to do that automatically)." 151 | 152 | info: 153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 154 | @echo "Running Texinfo files through makeinfo..." 155 | make -C $(BUILDDIR)/texinfo info 156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 157 | 158 | gettext: 159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 160 | @echo 161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 162 | 163 | changes: 164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 165 | @echo 166 | @echo "The overview file is in $(BUILDDIR)/changes." 167 | 168 | linkcheck: 169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 170 | @echo 171 | @echo "Link check complete; look for any errors in the above output " \ 172 | "or in $(BUILDDIR)/linkcheck/output.txt." 173 | 174 | doctest: 175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 176 | @echo "Testing of doctests in the sources finished, look at the " \ 177 | "results in $(BUILDDIR)/doctest/output.txt." 178 | 179 | coverage: 180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 181 | @echo "Testing of coverage in the sources finished, look at the " \ 182 | "results in $(BUILDDIR)/coverage/python.txt." 183 | 184 | xml: 185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 186 | @echo 187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 188 | 189 | pseudoxml: 190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 191 | @echo 192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 193 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 2> nul 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\py-googletrans.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\py-googletrans.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Googletrans 2 | =========== 3 | 4 | |GitHub license| |travis status| |Documentation Status| |PyPI version| 5 | |Coverage Status| |Code Climate| 6 | 7 | Googletrans is a **free** and **unlimited** python library that 8 | implemented Google Translate API. This uses the `Google Translate Ajax 9 | API `__ to make calls to such methods as 10 | detect and translate. 11 | 12 | Compatible with Python 2.7+ and 3.4+. (Note: Python 2 support will be dropped in the 13 | next major release.) 14 | 15 | For details refer to the `API 16 | Documentation `__. 17 | 18 | Features 19 | -------- 20 | 21 | - Fast and reliable - it uses the same servers that 22 | translate.google.com uses 23 | - Auto language detection 24 | - Bulk translations 25 | - Customizable service URL 26 | - Connection pooling (the advantage of using requests.Session) 27 | - HTTP/2 support 28 | 29 | TODO 30 | ~~~~ 31 | 32 | more features are coming soon. 33 | 34 | - Proxy support 35 | - Internal session management (for better bulk translations) 36 | 37 | HTTP/2 support 38 | ~~~~~~~~~~~~~~ 39 | 40 | This is a great deal for everyone! (up to 2x times faster in my test) If 41 | you want to get googletrans faster you should install 42 | `hyper `__ package. Googletrans will 43 | automatically detect if hyper is installed and if so, it will be used 44 | for http networking. 45 | 46 | How does this library work 47 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 48 | 49 | You may wonder why this library works properly, whereas other 50 | approaches such like goslate won't work since Google has updated its 51 | translation service recently with a ticket mechanism to prevent a lot of 52 | crawler programs. 53 | 54 | I eventually figure out a way to generate a ticket by reverse 55 | engineering on the `obfuscated and minified code used by Google to 56 | generate such 57 | token `__, 58 | and implemented on the top of Python. However, this could be blocked at 59 | any time. 60 | 61 | -------------- 62 | 63 | Installation 64 | ------------ 65 | 66 | To install, either use things like pip with the package "googletrans" 67 | or download the package and put the "googletrans" directory into your 68 | python path. Anyway, it is noteworthy that, this just requires two 69 | modules: requests and future. 70 | 71 | .. code:: bash 72 | 73 | $ pip install googletrans 74 | 75 | Basic Usage 76 | ----------- 77 | 78 | If source language is not given, google translate attempts to detect the 79 | source language. 80 | 81 | .. code:: python 82 | 83 | >>> from googletrans import Translator 84 | >>> translator = Translator() 85 | >>> translator.translate('안녕하세요.') 86 | # 87 | >>> translator.translate('안녕하세요.', dest='ja') 88 | # 89 | >>> translator.translate('veritas lux mea', src='la') 90 | # 91 | 92 | Customize service URL 93 | ~~~~~~~~~~~~~~~~~~~~~ 94 | 95 | You can use another google translate domain for translation. If multiple 96 | URLs are provided it then randomly chooses a domain. 97 | 98 | .. code:: python 99 | 100 | >>> from googletrans import Translator 101 | >>> translator = Translator(service_urls=[ 102 | 'translate.google.com', 103 | 'translate.google.co.kr', 104 | ]) 105 | 106 | Advanced Usage (Bulk) 107 | ~~~~~~~~~~~~~~~~~~~~~ 108 | 109 | Array can be used to translate a batch of strings in a single method 110 | call and a single HTTP session. The exact same method shown above work 111 | for arrays as well. 112 | 113 | .. code:: python 114 | 115 | >>> translations = translator.translate(['The quick brown fox', 'jumps over', 'the lazy dog'], dest='ko') 116 | >>> for translation in translations: 117 | ... print(translation.origin, ' -> ', translation.text) 118 | # The quick brown fox -> 빠른 갈색 여우 119 | # jumps over -> 이상 점프 120 | # the lazy dog -> 게으른 개 121 | 122 | Language detection 123 | ~~~~~~~~~~~~~~~~~~ 124 | 125 | The detect method, as its name implies, identifies the language used in 126 | a given sentence. 127 | 128 | .. code:: python 129 | 130 | >>> from googletrans import Translator 131 | >>> translator = Translator() 132 | >>> translator.detect('이 문장은 한글로 쓰여졌습니다.') 133 | # 134 | >>> translator.detect('この文章は日本語で書かれました。') 135 | # 136 | >>> translator.detect('This sentence is written in English.') 137 | # 138 | >>> translator.detect('Tiu frazo estas skribita en Esperanto.') 139 | # 140 | 141 | GoogleTrans as a command line application 142 | ----------------------------------------- 143 | 144 | .. code:: bash 145 | 146 | $ translate -h 147 | usage: translate [-h] [-d DEST] [-s SRC] [-c] text 148 | 149 | Python Google Translator as a command-line tool 150 | 151 | positional arguments: 152 | text The text you want to translate. 153 | 154 | optional arguments: 155 | -h, --help show this help message and exit 156 | -d DEST, --dest DEST The destination language you want to translate. 157 | (Default: en) 158 | -s SRC, --src SRC The source language you want to translate. (Default: 159 | auto) 160 | -c, --detect 161 | 162 | $ translate "veritas lux mea" -s la -d en 163 | [veritas] veritas lux mea 164 | -> 165 | [en] The truth is my light 166 | [pron.] The truth is my light 167 | 168 | $ translate -c "안녕하세요." 169 | [ko, 1] 안녕하세요. 170 | 171 | -------------- 172 | 173 | Note on library usage 174 | --------------------- 175 | 176 | DISCLAIMER: this is an unofficial library using the web API of translate.google.com 177 | and also is not associated with Google. 178 | 179 | - **The maximum character limit on a single text is 15k.** 180 | 181 | - Due to limitations of the web version of google translate, this API 182 | does not guarantee that the library would work properly at all times 183 | (so please use this library if you don't care about stability). 184 | 185 | - **Important:** If you want to use a stable API, I highly recommend you to use 186 | `Google's official translate 187 | API `__. 188 | 189 | - If you get HTTP 5xx error or errors like #6, it's probably because 190 | Google has banned your client IP address. 191 | 192 | -------------- 193 | 194 | Versioning 195 | ---------- 196 | 197 | This library follows `Semantic Versioning `__ from 198 | v2.0.0. Any release versioned 0.x.y is subject to backwards incompatible 199 | changes at any time. 200 | 201 | Submitting a Pull Request 202 | ------------------------- 203 | 204 | Contributions to this library are always welcome and highly encouraged 205 | :) 206 | 207 | 1. Fork this project. 208 | 2. Create a topic branch. 209 | 3. Implement your feature or bug fix. 210 | 4. Run ``pytest``. 211 | 5. Add a test for yout feature or bug fix. 212 | 6. Run step 4 again. If your changes are not 100% covered, go back to 213 | step 5. 214 | 7. Commit and push your changes. 215 | 8. Submit a pull request. 216 | 217 | -------------- 218 | 219 | License 220 | ------- 221 | 222 | Googletrans is licensed under the MIT License. The terms are as 223 | follows: 224 | 225 | :: 226 | 227 | The MIT License (MIT) 228 | 229 | Copyright (c) 2015 SuHun Han 230 | 231 | Permission is hereby granted, free of charge, to any person obtaining a copy 232 | of this software and associated documentation files (the "Software"), to deal 233 | in the Software without restriction, including without limitation the rights 234 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 235 | copies of the Software, and to permit persons to whom the Software is 236 | furnished to do so, subject to the following conditions: 237 | 238 | The above copyright notice and this permission notice shall be included in all 239 | copies or substantial portions of the Software. 240 | 241 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 242 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 243 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 244 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 245 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 246 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 247 | SOFTWARE. 248 | 249 | .. |GitHub license| image:: https://img.shields.io/github/license/mashape/apistatus.svg 250 | :target: http://opensource.org/licenses/MIT 251 | .. |travis status| image:: https://travis-ci.org/ssut/py-googletrans.svg?branch=master 252 | :target: https://travis-ci.org/ssut/py-googletrans 253 | .. |Documentation Status| image:: https://readthedocs.org/projects/py-googletrans/badge/?version=latest 254 | :target: https://readthedocs.org/projects/py-googletrans/?badge=latest 255 | .. |PyPI version| image:: https://badge.fury.io/py/googletrans.svg 256 | :target: http://badge.fury.io/py/googletrans 257 | .. |Coverage Status| image:: https://coveralls.io/repos/github/ssut/py-googletrans/badge.svg 258 | :target: https://coveralls.io/github/ssut/py-googletrans 259 | .. |Code Climate| image:: https://codeclimate.com/github/ssut/py-googletrans/badges/gpa.svg 260 | :target: https://codeclimate.com/github/ssut/py-googletrans 261 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Googletrans documentation build configuration file, created by 5 | # sphinx-quickstart on Fri Jun 5 17:51:30 2015. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | import datetime 17 | import sys 18 | import os 19 | import shlex 20 | 21 | sys.path.append('..') 22 | import googletrans 23 | 24 | # If extensions (or modules to document with autodoc) are in another directory, 25 | # add these directories to sys.path here. If the directory is relative to the 26 | # documentation root, use os.path.abspath to make it absolute, like shown here. 27 | #sys.path.insert(0, os.path.abspath('.')) 28 | 29 | # -- General configuration ------------------------------------------------ 30 | 31 | # If your documentation needs a minimal Sphinx version, state it here. 32 | #needs_sphinx = '1.0' 33 | 34 | # Add any Sphinx extension module names here, as strings. They can be 35 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 36 | # ones. 37 | extensions = [ 38 | 'sphinx.ext.autodoc', 39 | 'sphinx.ext.intersphinx', 40 | # 'sphinx.ext.viewcode', 41 | ] 42 | 43 | # Add any paths that contain templates here, relative to this directory. 44 | templates_path = ['_templates'] 45 | 46 | # The suffix(es) of source filenames. 47 | # You can specify multiple suffix as a list of string: 48 | # source_suffix = ['.rst', '.md'] 49 | source_suffix = '.rst' 50 | 51 | # The encoding of source files. 52 | #source_encoding = 'utf-8-sig' 53 | 54 | # The master toctree document. 55 | master_doc = 'index' 56 | 57 | # General information about the project. 58 | project = 'Googletrans' 59 | copyright = str(datetime.date.today().year) + ', SuHun Han (ssut)' 60 | author = 'SuHun Han (ssut)' 61 | 62 | # The version info for the project you're documenting, acts as replacement for 63 | # |version| and |release|, also used in various other places throughout the 64 | # built documents. 65 | # 66 | # The short X.Y version. 67 | version = googletrans.__version__ 68 | # The full version, including alpha/beta/rc tags. 69 | release = version 70 | 71 | # The language for content autogenerated by Sphinx. Refer to documentation 72 | # for a list of supported languages. 73 | # 74 | # This is also used if you do content translation via gettext catalogs. 75 | # Usually you set "language" from the command line for these cases. 76 | language = None 77 | 78 | # There are two options for replacing |today|: either, you set today to some 79 | # non-false value, then it is used: 80 | #today = '' 81 | # Else, today_fmt is used as the format for a strftime call. 82 | #today_fmt = '%B %d, %Y' 83 | 84 | # List of patterns, relative to source directory, that match files and 85 | # directories to ignore when looking for source files. 86 | exclude_patterns = ['_build', '_themes'] 87 | 88 | # The reST default role (used for this markup: `text`) to use for all 89 | # documents. 90 | #default_role = None 91 | 92 | # If true, '()' will be appended to :func: etc. cross-reference text. 93 | #add_function_parentheses = True 94 | 95 | # If true, the current module name will be prepended to all description 96 | # unit titles (such as .. function::). 97 | #add_module_names = True 98 | 99 | # If true, sectionauthor and moduleauthor directives will be shown in the 100 | # output. They are ignored by default. 101 | #show_authors = False 102 | 103 | # The name of the Pygments (syntax highlighting) style to use. 104 | pygments_style = 'sphinx' 105 | 106 | # A list of ignored prefixes for module index sorting. 107 | #modindex_common_prefix = [] 108 | 109 | # If true, keep warnings as "system message" paragraphs in the built documents. 110 | #keep_warnings = False 111 | 112 | # If true, `todo` and `todoList` produce output, else they produce nothing. 113 | todo_include_todos = False 114 | 115 | suppress_warnings = ['image.nonlocal_uri'] 116 | 117 | 118 | # -- Options for HTML output ---------------------------------------------- 119 | 120 | # The theme to use for HTML and HTML Help pages. See the documentation for 121 | # a list of builtin themes. 122 | html_theme = 'solar' 123 | 124 | # Theme options are theme-specific and customize the look and feel of a theme 125 | # further. For a list of options available for each theme, see the 126 | # documentation. 127 | #html_theme_options = {} 128 | 129 | # Add any paths that contain custom themes here, relative to this directory. 130 | html_theme_path = ['_themes'] 131 | 132 | # The name for this set of Sphinx documents. If None, it defaults to 133 | # " v documentation". 134 | #html_title = None 135 | 136 | # A shorter title for the navigation bar. Default is the same as html_title. 137 | #html_short_title = None 138 | 139 | # The name of an image file (relative to this directory) to place at the top 140 | # of the sidebar. 141 | #html_logo = None 142 | 143 | # The name of an image file (within the static path) to use as favicon of the 144 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 145 | # pixels large. 146 | #html_favicon = None 147 | 148 | # Add any paths that contain custom static files (such as style sheets) here, 149 | # relative to this directory. They are copied after the builtin static files, 150 | # so a file named "default.css" will overwrite the builtin "default.css". 151 | html_static_path = [] 152 | 153 | # Add any extra paths that contain custom files (such as robots.txt or 154 | # .htaccess) here, relative to this directory. These files are copied 155 | # directly to the root of the documentation. 156 | #html_extra_path = [] 157 | 158 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 159 | # using the given strftime format. 160 | #html_last_updated_fmt = '%b %d, %Y' 161 | 162 | # If true, SmartyPants will be used to convert quotes and dashes to 163 | # typographically correct entities. 164 | #html_use_smartypants = True 165 | 166 | # Custom sidebar templates, maps document names to template names. 167 | #html_sidebars = {} 168 | 169 | # Additional templates that should be rendered to pages, maps page names to 170 | # template names. 171 | #html_additional_pages = {} 172 | 173 | # If false, no module index is generated. 174 | #html_domain_indices = True 175 | 176 | # If false, no index is generated. 177 | html_use_index = False 178 | 179 | # If true, the index is split into individual pages for each letter. 180 | #html_split_index = False 181 | 182 | # If true, links to the reST sources are added to the pages. 183 | html_show_sourcelink = True 184 | 185 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 186 | #html_show_sphinx = True 187 | 188 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 189 | #html_show_copyright = True 190 | 191 | # If true, an OpenSearch description file will be output, and all pages will 192 | # contain a tag referring to it. The value of this option must be the 193 | # base URL from which the finished HTML is served. 194 | #html_use_opensearch = '' 195 | 196 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 197 | #html_file_suffix = None 198 | 199 | # Language to be used for generating the HTML full-text search index. 200 | # Sphinx supports the following languages: 201 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 202 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' 203 | #html_search_language = 'en' 204 | 205 | # A dictionary with options for the search language support, empty by default. 206 | # Now only 'ja' uses this config value 207 | #html_search_options = {'type': 'default'} 208 | 209 | # The name of a javascript file (relative to the configuration directory) that 210 | # implements a search results scorer. If empty, the default will be used. 211 | #html_search_scorer = 'scorer.js' 212 | 213 | # Output file base name for HTML help builder. 214 | htmlhelp_basename = 'Googletransdoc' 215 | 216 | # -- Options for LaTeX output --------------------------------------------- 217 | 218 | latex_elements = { 219 | # The paper size ('letterpaper' or 'a4paper'). 220 | #'papersize': 'letterpaper', 221 | 222 | # The font size ('10pt', '11pt' or '12pt'). 223 | #'pointsize': '10pt', 224 | 225 | # Additional stuff for the LaTeX preamble. 226 | #'preamble': '', 227 | 228 | # Latex figure (float) alignment 229 | #'figure_align': 'htbp', 230 | } 231 | 232 | # Grouping the document tree into LaTeX files. List of tuples 233 | # (source start file, target name, title, 234 | # author, documentclass [howto, manual, or own class]). 235 | latex_documents = [ 236 | (master_doc, 'Googletrans.tex', 'Googletrans Documentation', 237 | 'SuHun Han (ssut)', 'manual'), 238 | ] 239 | 240 | # The name of an image file (relative to this directory) to place at the top of 241 | # the title page. 242 | #latex_logo = None 243 | 244 | # For "manual" documents, if this is true, then toplevel headings are parts, 245 | # not chapters. 246 | #latex_use_parts = False 247 | 248 | # If true, show page references after internal links. 249 | #latex_show_pagerefs = False 250 | 251 | # If true, show URL addresses after external links. 252 | #latex_show_urls = False 253 | 254 | # Documents to append as an appendix to all manuals. 255 | #latex_appendices = [] 256 | 257 | # If false, no module index is generated. 258 | #latex_domain_indices = True 259 | 260 | 261 | # -- Options for manual page output --------------------------------------- 262 | 263 | # One entry per manual page. List of tuples 264 | # (source start file, name, description, authors, manual section). 265 | man_pages = [ 266 | (master_doc, 'Googletrans', 'Googletrans Documentation', 267 | [author], 1) 268 | ] 269 | 270 | # If true, show URL addresses after external links. 271 | #man_show_urls = False 272 | 273 | intersphinx_mapping = {'http://docs.python.org/': None} 274 | 275 | 276 | # -- Options for Texinfo output ------------------------------------------- 277 | 278 | # Grouping the document tree into Texinfo files. List of tuples 279 | # (source start file, target name, title, author, 280 | # dir menu entry, description, category) 281 | texinfo_documents = [ 282 | (master_doc, 'Googletrans', 'Googletrans Documentation', 283 | author, 'Googletrans', 'One line description of project.', 284 | 'Miscellaneous'), 285 | ] 286 | 287 | # Documents to append as an appendix to all manuals. 288 | #texinfo_appendices = [] 289 | 290 | # If false, no module index is generated. 291 | #texinfo_domain_indices = True 292 | 293 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 294 | #texinfo_show_urls = 'footnote' 295 | 296 | # If true, do not generate a @detailmenu in the "Top" node's menu. 297 | #texinfo_no_detailmenu = False 298 | 299 | autodoc_member_order = 'bysource' -------------------------------------------------------------------------------- /googletrans/client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | A Translation module. 4 | 5 | You can translate text using this module. 6 | """ 7 | import requests 8 | import random 9 | 10 | from googletrans import urls, utils 11 | from googletrans.adapters import TimeoutAdapter 12 | from googletrans.compat import PY3 13 | from googletrans.gtoken import TokenAcquirer 14 | from googletrans.constants import DEFAULT_USER_AGENT, LANGCODES, LANGUAGES, SPECIAL_CASES 15 | from googletrans.models import Translated, Detected 16 | 17 | 18 | EXCLUDES = ('en', 'ca', 'fr') 19 | 20 | 21 | class Translator(object): 22 | """Google Translate ajax API implementation class 23 | 24 | You have to create an instance of Translator to use this API 25 | 26 | :param service_urls: google translate url list. URLs will be used randomly. 27 | For example ``['translate.google.com', 'translate.google.co.kr']`` 28 | :type service_urls: a sequence of strings 29 | 30 | :param user_agent: the User-Agent header to send when making requests. 31 | :type user_agent: :class:`str` 32 | 33 | :param proxies: proxies configuration. 34 | Dictionary mapping protocol or protocol and host to the URL of the proxy 35 | For example ``{'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}`` 36 | :type proxies: dictionary 37 | 38 | :param timeout: Definition of timeout for Requests library. 39 | Will be used by every request. 40 | :type timeout: number or a double of numbers 41 | """ 42 | 43 | def __init__(self, service_urls=None, user_agent=DEFAULT_USER_AGENT, 44 | proxies=None, timeout=None): 45 | 46 | self.session = requests.Session() 47 | if proxies is not None: 48 | self.session.proxies = proxies 49 | self.session.headers.update({ 50 | 'User-Agent': user_agent, 51 | }) 52 | if timeout is not None: 53 | self.session.mount('https://', TimeoutAdapter(timeout)) 54 | self.session.mount('http://', TimeoutAdapter(timeout)) 55 | 56 | self.service_urls = service_urls or ['translate.google.com'] 57 | self.token_acquirer = TokenAcquirer(session=self.session, host=self.service_urls[0]) 58 | 59 | # Use HTTP2 Adapter if hyper is installed 60 | try: # pragma: nocover 61 | from hyper.contrib import HTTP20Adapter 62 | self.session.mount(urls.BASE, HTTP20Adapter()) 63 | except ImportError: # pragma: nocover 64 | pass 65 | 66 | def _pick_service_url(self): 67 | if len(self.service_urls) == 1: 68 | return self.service_urls[0] 69 | return random.choice(self.service_urls) 70 | 71 | def _translate(self, text, dest, src): 72 | if not PY3 and isinstance(text, str): # pragma: nocover 73 | text = text.decode('utf-8') 74 | 75 | token = self.token_acquirer.do(text) 76 | params = utils.build_params(query=text, src=src, dest=dest, 77 | token=token) 78 | url = urls.TRANSLATE.format(host=self._pick_service_url()) 79 | r = self.session.get(url, params=params) 80 | 81 | data = utils.format_json(r.text) 82 | return data 83 | 84 | def _parse_extra_data(self, data): 85 | response_parts_name_mapping = { 86 | 0: 'translation', 87 | 1: 'all-translations', 88 | 2: 'original-language', 89 | 5: 'possible-translations', 90 | 6: 'confidence', 91 | 7: 'possible-mistakes', 92 | 8: 'language', 93 | 11: 'synonyms', 94 | 12: 'definitions', 95 | 13: 'examples', 96 | 14: 'see-also', 97 | } 98 | 99 | extra = {} 100 | 101 | for index, category in response_parts_name_mapping.items(): 102 | extra[category] = data[index] if (index < len(data) and data[index]) else None 103 | 104 | return extra 105 | 106 | def translate(self, text, dest='en', src='auto'): 107 | """Translate text from source language to destination language 108 | 109 | :param text: The source text(s) to be translated. Batch translation is supported via sequence input. 110 | :type text: UTF-8 :class:`str`; :class:`unicode`; string sequence (list, tuple, iterator, generator) 111 | 112 | :param dest: The language to translate the source text into. 113 | The value should be one of the language codes listed in :const:`googletrans.LANGUAGES` 114 | or one of the language names listed in :const:`googletrans.LANGCODES`. 115 | :param dest: :class:`str`; :class:`unicode` 116 | 117 | :param src: The language of the source text. 118 | The value should be one of the language codes listed in :const:`googletrans.LANGUAGES` 119 | or one of the language names listed in :const:`googletrans.LANGCODES`. 120 | If a language is not specified, 121 | the system will attempt to identify the source language automatically. 122 | :param src: :class:`str`; :class:`unicode` 123 | 124 | :rtype: Translated 125 | :rtype: :class:`list` (when a list is passed) 126 | 127 | Basic usage: 128 | >>> from googletrans import Translator 129 | >>> translator = Translator() 130 | >>> translator.translate('안녕하세요.') 131 | 132 | >>> translator.translate('안녕하세요.', dest='ja') 133 | 134 | >>> translator.translate('veritas lux mea', src='la') 135 | 136 | 137 | Advanced usage: 138 | >>> translations = translator.translate(['The quick brown fox', 'jumps over', 'the lazy dog'], dest='ko') 139 | >>> for translation in translations: 140 | ... print(translation.origin, ' -> ', translation.text) 141 | The quick brown fox -> 빠른 갈색 여우 142 | jumps over -> 이상 점프 143 | the lazy dog -> 게으른 개 144 | """ 145 | dest = dest.lower().split('_', 1)[0] 146 | src = src.lower().split('_', 1)[0] 147 | 148 | if src != 'auto' and src not in LANGUAGES: 149 | if src in SPECIAL_CASES: 150 | src = SPECIAL_CASES[src] 151 | elif src in LANGCODES: 152 | src = LANGCODES[src] 153 | else: 154 | raise ValueError('invalid source language') 155 | 156 | if dest not in LANGUAGES: 157 | if dest in SPECIAL_CASES: 158 | dest = SPECIAL_CASES[dest] 159 | elif dest in LANGCODES: 160 | dest = LANGCODES[dest] 161 | else: 162 | raise ValueError('invalid destination language') 163 | 164 | if isinstance(text, list): 165 | result = [] 166 | for item in text: 167 | translated = self.translate(item, dest=dest, src=src) 168 | result.append(translated) 169 | return result 170 | 171 | origin = text 172 | data = self._translate(text, dest, src) 173 | 174 | # this code will be updated when the format is changed. 175 | translated = ''.join([d[0] if d[0] else '' for d in data[0]]) 176 | 177 | extra_data = self._parse_extra_data(data) 178 | 179 | # actual source language that will be recognized by Google Translator when the 180 | # src passed is equal to auto. 181 | try: 182 | src = data[2] 183 | except Exception: # pragma: nocover 184 | pass 185 | 186 | pron = origin 187 | try: 188 | pron = data[0][1][-2] 189 | except Exception: # pragma: nocover 190 | pass 191 | if not PY3 and isinstance(pron, unicode) and isinstance(origin, str): # pragma: nocover 192 | origin = origin.decode('utf-8') 193 | if dest in EXCLUDES and pron == origin: 194 | pron = translated 195 | 196 | # for python 2.x compatbillity 197 | if not PY3: # pragma: nocover 198 | if isinstance(src, str): 199 | src = src.decode('utf-8') 200 | if isinstance(dest, str): 201 | dest = dest.decode('utf-8') 202 | if isinstance(translated, str): 203 | translated = translated.decode('utf-8') 204 | 205 | # put final values into a new Translated object 206 | result = Translated(src=src, dest=dest, origin=origin, 207 | text=translated, pronunciation=pron, extra_data=extra_data) 208 | 209 | return result 210 | 211 | def detect(self, text): 212 | """Detect language of the input text 213 | 214 | :param text: The source text(s) whose language you want to identify. 215 | Batch detection is supported via sequence input. 216 | :type text: UTF-8 :class:`str`; :class:`unicode`; string sequence (list, tuple, iterator, generator) 217 | 218 | :rtype: Detected 219 | :rtype: :class:`list` (when a list is passed) 220 | 221 | Basic usage: 222 | >>> from googletrans import Translator 223 | >>> translator = Translator() 224 | >>> translator.detect('이 문장은 한글로 쓰여졌습니다.') 225 | 226 | >>> translator.detect('この文章は日本語で書かれました。') 227 | 228 | >>> translator.detect('This sentence is written in English.') 229 | 230 | >>> translator.detect('Tiu frazo estas skribita en Esperanto.') 231 | 232 | 233 | Advanced usage: 234 | >>> langs = translator.detect(['한국어', '日本語', 'English', 'le français']) 235 | >>> for lang in langs: 236 | ... print(lang.lang, lang.confidence) 237 | ko 1 238 | ja 0.92929292 239 | en 0.96954316 240 | fr 0.043500196 241 | """ 242 | if isinstance(text, list): 243 | result = [] 244 | for item in text: 245 | lang = self.detect(item) 246 | result.append(lang) 247 | return result 248 | 249 | data = self._translate(text, dest='en', src='auto') 250 | 251 | # actual source language that will be recognized by Google Translator when the 252 | # src passed is equal to auto. 253 | src = '' 254 | confidence = 0.0 255 | try: 256 | src = ''.join(data[8][0]) 257 | confidence = data[8][-2][0] 258 | except Exception: # pragma: nocover 259 | pass 260 | result = Detected(lang=src, confidence=confidence) 261 | 262 | return result 263 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "d324823904cf4298321e5b976f1787c3c9b73eb94ab413e96227109c0efcd33d" 5 | }, 6 | "host-environment-markers": { 7 | "implementation_name": "cpython", 8 | "implementation_version": "3.6.2", 9 | "os_name": "posix", 10 | "platform_machine": "x86_64", 11 | "platform_python_implementation": "CPython", 12 | "platform_release": "4.4.0-91-generic", 13 | "platform_system": "Linux", 14 | "platform_version": "#114-Ubuntu SMP Tue Aug 8 11:56:56 UTC 2017", 15 | "python_full_version": "3.6.2", 16 | "python_version": "3.6", 17 | "sys_platform": "linux" 18 | }, 19 | "pipfile-spec": 6, 20 | "requires": { 21 | "python_version": "3.6" 22 | }, 23 | "sources": [ 24 | { 25 | "name": "pypi", 26 | "url": "https://pypi.python.org/simple", 27 | "verify_ssl": true 28 | } 29 | ] 30 | }, 31 | "default": { 32 | "requests": { 33 | "hashes": [ 34 | "sha256:1a720e8862a41aa22e339373b526f508ef0c8988baf48b84d3fc891a8e237efb", 35 | "sha256:5722cd09762faa01276230270ff16af7acf7c5c45d623868d9ba116f15791ce8" 36 | ], 37 | "version": "==2.13.0" 38 | } 39 | }, 40 | "develop": { 41 | "argh": { 42 | "hashes": [ 43 | "sha256:a9b3aaa1904eeb78e32394cd46c6f37ac0fb4af6dc488daa58971bdc7d7fcaf3", 44 | "sha256:e9535b8c84dc9571a48999094fda7f33e63c3f1b74f3e5f3ac0105a58405bb65" 45 | ], 46 | "version": "==0.26.2" 47 | }, 48 | "certifi": { 49 | "hashes": [ 50 | "sha256:54a07c09c586b0e4c619f02a5e94e36619da8e2b053e20f594348c0611803704", 51 | "sha256:40523d2efb60523e113b44602298f0960e900388cf3bb6043f645cf57ea9e3f5" 52 | ], 53 | "version": "==2017.7.27.1" 54 | }, 55 | "chardet": { 56 | "hashes": [ 57 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", 58 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" 59 | ], 60 | "version": "==3.0.4" 61 | }, 62 | "colorama": { 63 | "hashes": [ 64 | "sha256:463f8483208e921368c9f306094eb6f725c6ca42b0f97e313cb5d5512459feda", 65 | "sha256:48eb22f4f8461b1df5734a074b57042430fb06e1d61bd1e11b078c0fe6d7a1f1" 66 | ], 67 | "version": "==0.3.9" 68 | }, 69 | "coverage": { 70 | "hashes": [ 71 | "sha256:c1456f66c536010cf9e4633a8853a9153e8fd588393695295afd4d0fc16c1d74", 72 | "sha256:97a7ec51cdde3a386e390b159b20f247ccb478084d925c75f1faa3d26c01335e", 73 | "sha256:83e955b975666b5a07d217135e7797857ce844eb340a99e46cc25525120417c4", 74 | "sha256:483ed14080c5301048128bb027b77978c632dd9e92e3ecb09b7e28f5b92abfcf", 75 | "sha256:ef574ab9640bcfa2f3c671831faf03f65788945fdf8efa4d4a1fffc034838e2a", 76 | "sha256:c5a205b4da3c624f5119dc4d84240789b5906bb8468902ec22dcc4aad8aa4638", 77 | "sha256:5dea90ed140e7fa9bc00463313f9bc4a6e6aff297b4969615e7a688615c4c4d2", 78 | "sha256:f9e83b39d29c2815a38e4118d776b482d4082b5bf9c9147fbc99a3f83abe480a", 79 | "sha256:700040c354f0230287906b1276635552a3def4b646e0145555bc9e2e5da9e365", 80 | "sha256:7f1eacae700c66c3d7362a433b228599c9d94a5a3a52613dddd9474e04deb6bc", 81 | "sha256:13ef9f799c8fb45c446a239df68034de3a6f3de274881b088bebd7f5661f79f8", 82 | "sha256:dfb011587e2b7299112f08a2a60d2601706aac9abde37aa1177ea825adaed923", 83 | "sha256:381be5d31d3f0d912334cf2c159bc7bea6bfe6b0e3df6061a3bf2bf88359b1f6", 84 | "sha256:83a477ac4f55a6ef59552683a0544d47b68a85ce6a80fd0ca6b3dc767f6495fb", 85 | "sha256:dfd35f1979da31bcabbe27bcf78d4284d69870731874af629082590023a77336", 86 | "sha256:9681efc2d310cfc53863cc6f63e88ebe7a48124550fa822147996cb09390b6ab", 87 | "sha256:53770b20ac5b4a12e99229d4bae57af0945be87cc257fce6c6c7571a39f0c5d4", 88 | "sha256:8801880d32f11b6df11c32a961e186774b4634ae39d7c43235f5a24368a85f07", 89 | "sha256:16db2c69a1acbcb3c13211e9f954e22b22a729909d81f983b6b9badacc466eda", 90 | "sha256:ef43a06a960b46c73c018704051e023ee6082030f145841ffafc8728039d5a88", 91 | "sha256:c3e2736664a6074fc9bd54fb643f5af0fc60bfedb2963b3d3f98c7450335e34c", 92 | "sha256:17709e22e4c9f5412ba90f446fb13b245cc20bf4a60377021bbff6c0f1f63e7c", 93 | "sha256:a2f7106d1167825c4115794c2ba57cc3b15feb6183db5328fa66f94c12902d8b", 94 | "sha256:2a08e978f402696c6956eee9d1b7e95d3ad042959b71bafe1f3e4557cbd6e0ac", 95 | "sha256:57f510bb16efaec0b6f371b64a8000c62e7e3b3e48e8b0a5745ade078d849814", 96 | "sha256:0f1883eab9c19aa243f51308751b8a2a547b9b817b721cc0ecf3efb99fafbea7", 97 | "sha256:e00fe141e22ce6e9395aa24d862039eb180c6b7e89df0bbaf9765e9aebe560a9", 98 | "sha256:ec596e4401553caa6dd2e3349ce47f9ef82c1f1bcba5d8ac3342724f0df8d6ff", 99 | "sha256:c820a533a943ebc860acc0ce6a00dd36e0fdf2c6f619ff8225755169428c5fa2", 100 | "sha256:b7f7283eb7badd2b8a9c6a9d6eeca200a0a24db6be79baee2c11398f978edcaa", 101 | "sha256:a5ed27ad3e8420b2d6b625dcbd3e59488c14ccc06030167bcf14ffb0f4189b77", 102 | "sha256:d7b70b7b4eb14d0753d33253fe4f121ca99102612e2719f0993607deb30c6f33", 103 | "sha256:4047dc83773869701bde934fb3c4792648eda7c0e008a77a0aec64157d246801", 104 | "sha256:7a9c44400ee0f3b4546066e0710e1250fd75831adc02ab99dda176ad8726f424", 105 | "sha256:0f649e68db74b1b5b8ca4161d08eb2b8fa8ae11af1ebfb80e80e112eb0ef5300", 106 | "sha256:52964fae0fafef8bd283ad8e9a9665205a9fdf912535434defc0ec3def1da26b", 107 | "sha256:36aa6c8db83bc27346ddcd8c2a60846a7178ecd702672689d3ea1828eb1a4d11", 108 | "sha256:9824e15b387d331c0fc0fef905a539ab69784368a1d6ac3db864b4182e520948", 109 | "sha256:4a678e1b9619a29c51301af61ab84122e2f8cc7a0a6b40854b808ac6be604300", 110 | "sha256:8bb7c8dca54109b61013bc4114d96effbf10dea136722c586bce3a5d9fc4e730", 111 | "sha256:1a41d621aa9b6ab6457b557a754d50aaff0813fad3453434de075496fca8a183", 112 | "sha256:0fa423599fc3d9e18177f913552cdb34a8d9ad33efcf52a98c9d4b644edb42c5", 113 | "sha256:e61a4ba0b2686040cb4828297c7e37bcaf3a1a1c0bc0dbe46cc789dde51a80fa", 114 | "sha256:ce9ef0fc99d11d418662e36fd8de6d71b19ec87c2eab961a117cc9d087576e72" 115 | ], 116 | "version": "==4.4.1" 117 | }, 118 | "coveralls": { 119 | "hashes": [ 120 | "sha256:84dd8c88c5754e8db70a682f537e2781366064aa3cdd6b24c2dcecbd3181187c", 121 | "sha256:510682001517bcca1def9f6252df6ce730fcb9831c62d9fff7c7d55b6fdabdf3" 122 | ], 123 | "version": "==1.2.0" 124 | }, 125 | "docopt": { 126 | "hashes": [ 127 | "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491" 128 | ], 129 | "version": "==0.6.2" 130 | }, 131 | "future": { 132 | "hashes": [ 133 | "sha256:e39ced1ab767b5936646cedba8bcce582398233d6a627067d4c6a454c90cfedb" 134 | ], 135 | "version": "==0.16.0" 136 | }, 137 | "idna": { 138 | "hashes": [ 139 | "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4", 140 | "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f" 141 | ], 142 | "version": "==2.6" 143 | }, 144 | "pathtools": { 145 | "hashes": [ 146 | "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0" 147 | ], 148 | "version": "==0.1.2" 149 | }, 150 | "py": { 151 | "hashes": [ 152 | "sha256:2ccb79b01769d99115aa600d7eed99f524bf752bba8f041dc1c184853514655a", 153 | "sha256:0f2d585d22050e90c7d293b6451c83db097df77871974d90efd5a30dc12fcde3" 154 | ], 155 | "version": "==1.4.34" 156 | }, 157 | "pytest": { 158 | "hashes": [ 159 | "sha256:b84f554f8ddc23add65c411bf112b2d88e2489fd45f753b1cae5936358bdf314", 160 | "sha256:f46e49e0340a532764991c498244a60e3a37d7424a532b3ff1a6a7653f1a403a" 161 | ], 162 | "version": "==3.2.2" 163 | }, 164 | "pytest-testmon": { 165 | "hashes": [ 166 | "sha256:a8e71249b53955030c98f986abaa6d9378e97606ee8c9606d4ad6c17cff8d4de" 167 | ], 168 | "version": "==0.9.6" 169 | }, 170 | "pytest-watch": { 171 | "hashes": [ 172 | "sha256:29941f6ff74e6d85cc0796434a5cbc27ebe51e91ed24fd0757fad5cc6fd3d491" 173 | ], 174 | "version": "==4.1.0" 175 | }, 176 | "pyyaml": { 177 | "hashes": [ 178 | "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f", 179 | "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736", 180 | "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269", 181 | "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8", 182 | "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4", 183 | "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1", 184 | "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab", 185 | "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3", 186 | "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8", 187 | "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6", 188 | "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca", 189 | "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8", 190 | "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608", 191 | "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7" 192 | ], 193 | "version": "==3.12" 194 | }, 195 | "requests": { 196 | "hashes": [ 197 | "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", 198 | "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" 199 | ], 200 | "version": "==2.18.4" 201 | }, 202 | "urllib3": { 203 | "hashes": [ 204 | "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", 205 | "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" 206 | ], 207 | "version": "==1.22" 208 | }, 209 | "watchdog": { 210 | "hashes": [ 211 | "sha256:7e65882adb7746039b6f3876ee174952f8eaaa34491ba34333ddf1fe35de4162" 212 | ], 213 | "version": "==0.8.3" 214 | } 215 | } 216 | } 217 | --------------------------------------------------------------------------------