├── .coveragerc ├── .gitignore ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── pypapago ├── __init__.py └── translator.py ├── setup.py └── test.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source=pypapago 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/linux,macos,python,pycharm+all 3 | # Edit at https://www.gitignore.io/?templates=linux,macos,python,pycharm+all 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### macOS ### 21 | # General 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Icon must end with two \r 27 | Icon 28 | 29 | # Thumbnails 30 | ._* 31 | 32 | # Files that might appear in the root of a volume 33 | .DocumentRevisions-V100 34 | .fseventsd 35 | .Spotlight-V100 36 | .TemporaryItems 37 | .Trashes 38 | .VolumeIcon.icns 39 | .com.apple.timemachine.donotpresent 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | 48 | ### PyCharm+all ### 49 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 50 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 51 | 52 | # User-specific stuff 53 | .idea/**/workspace.xml 54 | .idea/**/tasks.xml 55 | .idea/**/usage.statistics.xml 56 | .idea/**/dictionaries 57 | .idea/**/shelf 58 | 59 | # Generated files 60 | .idea/**/contentModel.xml 61 | 62 | # Sensitive or high-churn files 63 | .idea/**/dataSources/ 64 | .idea/**/dataSources.ids 65 | .idea/**/dataSources.local.xml 66 | .idea/**/sqlDataSources.xml 67 | .idea/**/dynamic.xml 68 | .idea/**/uiDesigner.xml 69 | .idea/**/dbnavigator.xml 70 | 71 | # Gradle 72 | .idea/**/gradle.xml 73 | .idea/**/libraries 74 | 75 | # Gradle and Maven with auto-import 76 | # When using Gradle or Maven with auto-import, you should exclude module files, 77 | # since they will be recreated, and may cause churn. Uncomment if using 78 | # auto-import. 79 | # .idea/modules.xml 80 | # .idea/*.iml 81 | # .idea/modules 82 | # *.iml 83 | # *.ipr 84 | 85 | # CMake 86 | cmake-build-*/ 87 | 88 | # Mongo Explorer plugin 89 | .idea/**/mongoSettings.xml 90 | 91 | # File-based project format 92 | *.iws 93 | 94 | # IntelliJ 95 | out/ 96 | 97 | # mpeltonen/sbt-idea plugin 98 | .idea_modules/ 99 | 100 | # JIRA plugin 101 | atlassian-ide-plugin.xml 102 | 103 | # Cursive Clojure plugin 104 | .idea/replstate.xml 105 | 106 | # Crashlytics plugin (for Android Studio and IntelliJ) 107 | com_crashlytics_export_strings.xml 108 | crashlytics.properties 109 | crashlytics-build.properties 110 | fabric.properties 111 | 112 | # Editor-based Rest Client 113 | .idea/httpRequests 114 | 115 | # Android studio 3.1+ serialized cache file 116 | .idea/caches/build_file_checksums.ser 117 | 118 | ### PyCharm+all Patch ### 119 | # Ignores the whole .idea folder and all .iml files 120 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 121 | 122 | .idea/ 123 | 124 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 125 | 126 | *.iml 127 | modules.xml 128 | .idea/misc.xml 129 | *.ipr 130 | 131 | # Sonarlint plugin 132 | .idea/sonarlint 133 | 134 | ### Python ### 135 | # Byte-compiled / optimized / DLL files 136 | __pycache__/ 137 | *.py[cod] 138 | *$py.class 139 | 140 | # C extensions 141 | *.so 142 | 143 | # Distribution / packaging 144 | .Python 145 | build/ 146 | develop-eggs/ 147 | dist/ 148 | downloads/ 149 | eggs/ 150 | .eggs/ 151 | lib/ 152 | lib64/ 153 | parts/ 154 | sdist/ 155 | var/ 156 | wheels/ 157 | pip-wheel-metadata/ 158 | share/python-wheels/ 159 | *.egg-info/ 160 | .installed.cfg 161 | *.egg 162 | MANIFEST 163 | 164 | # PyInstaller 165 | # Usually these files are written by a python script from a template 166 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 167 | *.manifest 168 | *.spec 169 | 170 | # Installer logs 171 | pip-log.txt 172 | pip-delete-this-directory.txt 173 | 174 | # Unit test / coverage reports 175 | htmlcov/ 176 | .tox/ 177 | .nox/ 178 | .coverage 179 | .coverage.* 180 | .cache 181 | nosetests.xml 182 | coverage.xml 183 | *.cover 184 | .hypothesis/ 185 | .pytest_cache/ 186 | 187 | # Translations 188 | *.mo 189 | *.pot 190 | 191 | # Django stuff: 192 | *.log 193 | local_settings.py 194 | db.sqlite3 195 | db.sqlite3-journal 196 | 197 | # Flask stuff: 198 | instance/ 199 | .webassets-cache 200 | 201 | # Scrapy stuff: 202 | .scrapy 203 | 204 | # Sphinx documentation 205 | docs/_build/ 206 | 207 | # PyBuilder 208 | target/ 209 | 210 | # Jupyter Notebook 211 | .ipynb_checkpoints 212 | 213 | # IPython 214 | profile_default/ 215 | ipython_config.py 216 | 217 | # pyenv 218 | .python-version 219 | 220 | # pipenv 221 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 222 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 223 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 224 | # install all needed dependencies. 225 | #Pipfile.lock 226 | 227 | # celery beat schedule file 228 | celerybeat-schedule 229 | 230 | # SageMath parsed files 231 | *.sage.py 232 | 233 | # Environments 234 | .env 235 | .venv 236 | env/ 237 | venv/ 238 | ENV/ 239 | env.bak/ 240 | venv.bak/ 241 | 242 | # Spyder project settings 243 | .spyderproject 244 | .spyproject 245 | 246 | # Rope project settings 247 | .ropeproject 248 | 249 | # mkdocs documentation 250 | /site 251 | 252 | # mypy 253 | .mypy_cache/ 254 | .dmypy.json 255 | dmypy.json 256 | 257 | # Pyre type checker 258 | .pyre/ 259 | 260 | # End of https://www.gitignore.io/api/linux,macos,python,pycharm+all -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Junbum Lee 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 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | requests = "<2.23" 8 | tqdm = "*" 9 | 10 | [dev-packages] 11 | codecov = "*" 12 | 13 | [requires] 14 | python_version = "3.7" 15 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "acbbfbdad534b2a861672ecdcc5237da47861066931939a9a9c4a5360375e109" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", 22 | "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" 23 | ], 24 | "version": "==2019.6.16" 25 | }, 26 | "chardet": { 27 | "hashes": [ 28 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 29 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 30 | ], 31 | "version": "==3.0.4" 32 | }, 33 | "idna": { 34 | "hashes": [ 35 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 36 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 37 | ], 38 | "version": "==2.8" 39 | }, 40 | "requests": { 41 | "hashes": [ 42 | "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 43 | "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 44 | ], 45 | "index": "pypi", 46 | "version": "==2.22.0" 47 | }, 48 | "tqdm": { 49 | "hashes": [ 50 | "sha256:14a285392c32b6f8222ecfbcd217838f88e11630affe9006cd0e94c7eff3cb61", 51 | "sha256:25d4c0ea02a305a688e7e9c2cdc8f862f989ef2a4701ab28ee963295f5b109ab" 52 | ], 53 | "index": "pypi", 54 | "version": "==4.32.2" 55 | }, 56 | "urllib3": { 57 | "hashes": [ 58 | "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", 59 | "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" 60 | ], 61 | "version": "==1.25.3" 62 | } 63 | }, 64 | "develop": { 65 | "certifi": { 66 | "hashes": [ 67 | "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", 68 | "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" 69 | ], 70 | "version": "==2019.6.16" 71 | }, 72 | "chardet": { 73 | "hashes": [ 74 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 75 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 76 | ], 77 | "version": "==3.0.4" 78 | }, 79 | "codecov": { 80 | "hashes": [ 81 | "sha256:8ed8b7c6791010d359baed66f84f061bba5bd41174bf324c31311e8737602788", 82 | "sha256:ae00d68e18d8a20e9c3288ba3875ae03db3a8e892115bf9b83ef20507732bed4" 83 | ], 84 | "index": "pypi", 85 | "version": "==2.0.15" 86 | }, 87 | "coverage": { 88 | "hashes": [ 89 | "sha256:3684fabf6b87a369017756b551cef29e505cb155ddb892a7a29277b978da88b9", 90 | "sha256:39e088da9b284f1bd17c750ac672103779f7954ce6125fd4382134ac8d152d74", 91 | "sha256:3c205bc11cc4fcc57b761c2da73b9b72a59f8d5ca89979afb0c1c6f9e53c7390", 92 | "sha256:465ce53a8c0f3a7950dfb836438442f833cf6663d407f37d8c52fe7b6e56d7e8", 93 | "sha256:48020e343fc40f72a442c8a1334284620f81295256a6b6ca6d8aa1350c763bbe", 94 | "sha256:5296fc86ab612ec12394565c500b412a43b328b3907c0d14358950d06fd83baf", 95 | "sha256:5f61bed2f7d9b6a9ab935150a6b23d7f84b8055524e7be7715b6513f3328138e", 96 | "sha256:68a43a9f9f83693ce0414d17e019daee7ab3f7113a70c79a3dd4c2f704e4d741", 97 | "sha256:6b8033d47fe22506856fe450470ccb1d8ba1ffb8463494a15cfc96392a288c09", 98 | "sha256:7ad7536066b28863e5835e8cfeaa794b7fe352d99a8cded9f43d1161be8e9fbd", 99 | "sha256:7bacb89ccf4bedb30b277e96e4cc68cd1369ca6841bde7b005191b54d3dd1034", 100 | "sha256:839dc7c36501254e14331bcb98b27002aa415e4af7ea039d9009409b9d2d5420", 101 | "sha256:8f9a95b66969cdea53ec992ecea5406c5bd99c9221f539bca1e8406b200ae98c", 102 | "sha256:932c03d2d565f75961ba1d3cec41ddde00e162c5b46d03f7423edcb807734eab", 103 | "sha256:988529edadc49039d205e0aa6ce049c5ccda4acb2d6c3c5c550c17e8c02c05ba", 104 | "sha256:998d7e73548fe395eeb294495a04d38942edb66d1fa61eb70418871bc621227e", 105 | "sha256:9de60893fb447d1e797f6bf08fdf0dbcda0c1e34c1b06c92bd3a363c0ea8c609", 106 | "sha256:9e80d45d0c7fcee54e22771db7f1b0b126fb4a6c0a2e5afa72f66827207ff2f2", 107 | "sha256:a545a3dfe5082dc8e8c3eb7f8a2cf4f2870902ff1860bd99b6198cfd1f9d1f49", 108 | "sha256:a5d8f29e5ec661143621a8f4de51adfb300d7a476224156a39a392254f70687b", 109 | "sha256:aca06bfba4759bbdb09bf52ebb15ae20268ee1f6747417837926fae990ebc41d", 110 | "sha256:bb23b7a6fd666e551a3094ab896a57809e010059540ad20acbeec03a154224ce", 111 | "sha256:bfd1d0ae7e292105f29d7deaa9d8f2916ed8553ab9d5f39ec65bcf5deadff3f9", 112 | "sha256:c62ca0a38958f541a73cf86acdab020c2091631c137bd359c4f5bddde7b75fd4", 113 | "sha256:c709d8bda72cf4cd348ccec2a4881f2c5848fd72903c185f363d361b2737f773", 114 | "sha256:c968a6aa7e0b56ecbd28531ddf439c2ec103610d3e2bf3b75b813304f8cb7723", 115 | "sha256:df785d8cb80539d0b55fd47183264b7002077859028dfe3070cf6359bf8b2d9c", 116 | "sha256:f406628ca51e0ae90ae76ea8398677a921b36f0bd71aab2099dfed08abd0322f", 117 | "sha256:f46087bbd95ebae244a0eda01a618aff11ec7a069b15a3ef8f6b520db523dcf1", 118 | "sha256:f8019c5279eb32360ca03e9fac40a12667715546eed5c5eb59eb381f2f501260", 119 | "sha256:fc5f4d209733750afd2714e9109816a29500718b32dd9a5db01c0cb3a019b96a" 120 | ], 121 | "version": "==4.5.3" 122 | }, 123 | "idna": { 124 | "hashes": [ 125 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 126 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 127 | ], 128 | "version": "==2.8" 129 | }, 130 | "requests": { 131 | "hashes": [ 132 | "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 133 | "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 134 | ], 135 | "index": "pypi", 136 | "version": "==2.22.0" 137 | }, 138 | "urllib3": { 139 | "hashes": [ 140 | "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", 141 | "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" 142 | ], 143 | "version": "==1.25.3" 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS PROJECT is ARCHIVED! 2 | 3 | This project is no longer maintained by the author. 4 | No additional support is provided. 5 | 6 | If you're still interested in the Papago's translation service, plz follow the direction below: 7 | 8 | https://developers.naver.com/docs/papago/papago-nmt-overview.md 9 | 10 | --- 11 | 12 | # pypapago [![codecov](https://codecov.io/gh/Beomi/pypapago/branch/master/graph/badge.svg)](https://codecov.io/gh/Beomi/pypapago) 13 | 14 | Unofficial python wrapper for [papago translate service](https://papago.naver.com). 15 | 16 | ## Install 17 | 18 | ```bash 19 | pip install -U pypapago 20 | ``` 21 | 22 | ## Usage 23 | 24 | ### Basic usage (English to Korean) 25 | 26 | ```python 27 | from pypapago import Translator 28 | 29 | translator = Translator() 30 | 31 | result = translator.translate('I am GROOT') 32 | print(result) # 나는 그루트다 33 | ``` 34 | 35 | ### Set Source/Target Language 36 | 37 | ```python 38 | from pypapago import Translator 39 | 40 | translator = Translator() 41 | 42 | result = translator.translate( 43 | '카카오는 파파고를 좋아해', 44 | source='ko', 45 | target='en', 46 | ) 47 | print(result) # Kakao likes papago. 48 | ``` 49 | 50 | #### Supported Language Codes 51 | 52 | Code | Desc 53 | --|-- 54 | ko | Korean 55 | en | English 56 | ja | Japanese 57 | zh-CN | Chinese 58 | zh-TW | Chinese traditional 59 | es | Spanish 60 | fr | French 61 | vi | Vietnamese 62 | th | Thai 63 | id | Indonesia 64 | 65 | 66 | ### Bulk Translation 67 | 68 | Parallel bulk translation with Multiprocessing. 69 | 70 | ```python 71 | from pypapago import Translator 72 | 73 | translator = Translator() 74 | 75 | result = translator.bulk_translate(['apple', 'banana']) 76 | print(result) # ['사과', '바나나'] 77 | ``` 78 | 79 | You can also set how many workers to run manually. 80 | 81 | (The more workers make your code faster but requires more system resources.) 82 | 83 | Default to CPU Cores (HyperThreading = x2) 84 | 85 | - ex) Run with 2cores 86 | 87 | ```python 88 | from pypapago import Translator 89 | 90 | translator = Translator() 91 | 92 | result = translator.bulk_translate( 93 | ['apple', 'banana'], 94 | workers=2 95 | ) 96 | print(result) # ['사과', '바나나'] 97 | ``` 98 | 99 | ### Verbose output 100 | 101 | If you need raw result from papago API, you can set `verbose` to `True`. 102 | 103 | ```python 104 | from pypapago import Translator 105 | 106 | translator = Translator() 107 | 108 | result = translator.translate('I am GROOT', verbose=True) 109 | print(result) # RAW JSON Result 110 | #{'delay': 400, 111 | # 'delaySmt': 400, 112 | # 'dict': {'items': [{'entry': 'I', 113 | # ... 114 | # 'translatedText': '나는 그루트다'} 115 | #} 116 | ``` 117 | 118 | Detail results may change. 119 | -------------------------------------------------------------------------------- /pypapago/__init__.py: -------------------------------------------------------------------------------- 1 | from .translator import Translator 2 | -------------------------------------------------------------------------------- /pypapago/translator.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import re 3 | import requests 4 | from multiprocessing import Pool, cpu_count 5 | from functools import partial 6 | from tqdm.auto import tqdm 7 | 8 | 9 | class Translator: 10 | """ 11 | Main Translator Class 12 | """ 13 | 14 | def __init__(self, regex_pattern=None, headers=None): 15 | self.regex_pattern = re.compile(regex_pattern or '[가-힣]+') 16 | self.headers = headers or { 17 | 'device-type': 'pc', 18 | 'origin': 'https://papago.naver.com', 19 | 'accept-encoding': 'gzip, deflate, br', 20 | 'accept-language': 'ko', 21 | 'authority': 'papago.naver.com', 22 | 'pragma': 'no-cache', 23 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko)\ 24 | Chrome/75.0.3770.100 Safari/537.36', 25 | 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 26 | 'accept': 'application/json', 27 | 'cache-control': 'no-cache', 28 | 'x-apigw-partnerid': 'papago', 29 | 'referer': 'https://papago.naver.com/', 30 | 'dnt': '1', 31 | } 32 | self.SECRET_KEY = 'rlWxMKMcL2IWMPV6ImUwMWMwZWFkLWMyNDUtNDg2YS05ZTdiLWExZTZmNzc2OTc0MyIsImRpY3QiOnRydWUsImRpY3REaXNwbGF5Ijoz' 33 | self.QUERY_KEY = '0,"honorific":false,"instant":false,"source":"{source}","target":"{target}","text":"{query}"}}' 34 | 35 | @staticmethod 36 | def string_to_base64(s): 37 | """ 38 | Generate Base64 Encoded string 39 | :param s: Origin Text (UTF-8) 40 | :return: B64 encoded text (B64, still UTF-8 string) 41 | """ 42 | return base64.b64encode(s.encode('utf-8')).decode('utf-8') 43 | 44 | def translate(self, query, source='en', target='ko', verbose=False): 45 | """ 46 | Main Translate function 47 | :param query: Original Text to translate 48 | :param source: Source(Original) text language [en, ko] 49 | :param target: Target text language [en, ko] 50 | :param verbose: Return verbose json data. Default: False 51 | :return: Translated text 52 | """ 53 | data = { 54 | 'data': self.SECRET_KEY + self.string_to_base64( 55 | self.QUERY_KEY.format(source=source, target=target, query=query) 56 | ) 57 | } 58 | response = requests.post('https://papago.naver.com/apis/n2mt/translate', headers=self.headers, data=data) 59 | if not verbose: 60 | return response.json()['translatedText'] 61 | return response.json() 62 | 63 | def bulk_translate(self, queries, source='en', target='ko', workers=None, verbose=False): 64 | """ 65 | Call Translate function in parallel 66 | :param queries: List of query texts 67 | :param source: Source(Original) text language [en, ko] 68 | :param target: Target text language [en, ko] 69 | :param workers: Python multiprocessing workers. Default: vCPU cores 70 | :param verbose: Return verbose json data. Default: False 71 | :return: List of translated texts 72 | """ 73 | with Pool(workers or cpu_count()) as pool: 74 | result = list(tqdm(pool.imap( 75 | func=partial(self.translate, source=source, target=target, verbose=verbose), 76 | iterable=queries 77 | ), total=len(queries))) 78 | pool.close() 79 | pool.join() 80 | return result 81 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='pypapago', 5 | version='0.1.1.1', 6 | url='https://github.com/Beomi/pypapago', 7 | license='MIT', 8 | author='Junbum Lee', 9 | author_email='jun@beomi.net', 10 | description='[Unofficial] Python wrapper for Papago translation service', 11 | packages=find_packages(exclude=['tests']), 12 | long_description=open('README.md').read(), 13 | long_description_content_type="text/markdown", 14 | classifiers=[ 15 | "Programming Language :: Python :: 3", 16 | "License :: OSI Approved :: MIT License", 17 | "Operating System :: OS Independent", 18 | ], 19 | install_requires=['requests<3', 'tqdm<5'], 20 | ) 21 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import re 3 | 4 | from pypapago import Translator 5 | 6 | 7 | class TestResponse(unittest.TestCase): 8 | def setUp(self): 9 | self.VERBOSE_KEYS = {'srcLangType', 'tarLangType', 'translatedText', 'dict', 'tarDict', 'tlit', 'delay', 10 | 'delaySmt', 'langDetection'} 11 | 12 | def test_create_instance(self): 13 | sample_re_pattern = re.compile('w+') 14 | translator = Translator(headers={'test': 1234}, regex_pattern=sample_re_pattern) 15 | assert translator.headers == {'test': 1234} 16 | assert translator.regex_pattern == sample_re_pattern 17 | 18 | def test_default_request(self): 19 | translator = Translator() 20 | assert translator.translate('Apple') == '사과' 21 | 22 | def test_verbose_request(self): 23 | translator = Translator() 24 | assert set( 25 | translator.translate('사과', verbose=True, source='ko', target='en').keys() 26 | ) == self.VERBOSE_KEYS 27 | 28 | def test_bulk_translate(self): 29 | translator = Translator() 30 | assert translator.bulk_translate(['apple', 'banana'] * 10) == ['사과', '바나나'] * 10 31 | # Test again with same Instance object 32 | assert translator.bulk_translate(['apple', 'banana'] * 10) == ['사과', '바나나'] * 10 33 | 34 | 35 | if __name__ == '__main__': 36 | unittest.main() 37 | --------------------------------------------------------------------------------