Repository: Animenosekai/translate (language: python)

Issue:
Got another translator for you.
https://www.translatedict.com

its pretty good, you should add it to this also.


Discussion / hints:
I think this is not a serious translator, the site is somehow not beautifully made, all crooked, most likely they use a third-party API of another translator
HAHAHAHHA! LMAO, they simply use the Microsoft Translate API:
![image](https://user-images.githubusercontent.com/61125068/133185622-008698e3-b8eb-47ae-84f8-1901dc55b6d7.png)

It seems like it could be using some results from google translate aswell eh? Could be wrong but every language I tested seemed to work...lol with this url

https://t3.translatedict.com/1.php?p1=auto&p2=en&p3=bonjour

just doesn't say the language detected.
Could be used to add another translate endpoint though (TranslateArray) to the BingTranslator
Similar implementation: https://github.com/neverneverendup/Translator/blob/master/Bing.py

Fix patch:
diff --git a/.gitignore b/.gitignore
index 898beb4..3a152dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -127,8 +127,7 @@ dmypy.json
 .DS_Store
 
 # translatepy translate service session files
-.bing_translatepy
-.microsoft_translatepy
+.translatepy/sessions/*
 
 .vscode/*
-yuno/*
\ No newline at end of file
+yuno/*
diff --git a/hello.mp3 b/hello.mp3
deleted file mode 100644
index 3262b89..0000000
Binary files a/hello.mp3 and /dev/null differ
diff --git a/requirements.txt b/requirements.txt
index 29549fc..134c0ed 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,16 +1,16 @@
-ansicon==1.89.0 ; python_version >= "3.8" and python_version < "4.0" and platform_system == "Windows" \
+ansicon==1.89.0 ; python_version >= "3.10" and python_version < "4.0" and platform_system == "Windows" \
     --hash=sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1 \
     --hash=sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec
-beautifulsoup4==4.12.2 ; python_version >= "3.8" and python_version < "4.0" \
+beautifulsoup4==4.12.2 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da \
     --hash=sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a
-blessed==1.20.0 ; python_version >= "3.8" and python_version < "4.0" \
+blessed==1.20.0 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:0c542922586a265e699188e52d5f5ac5ec0dd517e5a1041d90d2bbf23f906058 \
     --hash=sha256:2cdd67f8746e048f00df47a2880f4d6acbcdb399031b604e34ba8f71d5787680
-blinker==1.6.2 ; python_version >= "3.8" and python_version < "4.0" \
+blinker==1.6.2 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213 \
     --hash=sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0
-brotli==1.0.9 ; python_version >= "3.8" and python_version < "4.0" \
+brotli==1.0.9 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:02177603aaca36e1fd21b091cb742bb3b305a569e2402f1ca38af471777fb019 \
     --hash=sha256:11d3283d89af7033236fa4e73ec2cbe743d4f6a81d41bd234f24bf63dde979df \
     --hash=sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d \
@@ -93,10 +93,13 @@ brotli==1.0.9 ; python_version >= "3.8" and python_version < "4.0" \
     --hash=sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806 \
     --hash=sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3 \
     --hash=sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1
-certifi==2023.7.22 ; python_version >= "3.8" and python_version < "4.0" \
+cain==1.1.1 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:67528e6f70ae725befe831824fc35a741c160fe4204afdc764b2dc35348d80d3 \
+    --hash=sha256:e2faeee41ad97de527e2d3bc459b3153da5d55f4da4a55d10136bdb91c77e9cb
+certifi==2023.7.22 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \
     --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9
-charset-normalizer==3.2.0 ; python_version >= "3.8" and python_version < "4.0" \
+charset-normalizer==3.2.0 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96 \
     --hash=sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c \
     --hash=sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710 \
@@ -172,46 +175,46 @@ charset-normalizer==3.2.0 ; python_version >= "3.8" and python_version < "4.0" \
     --hash=sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c \
     --hash=sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac \
     --hash=sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa
-click==8.1.6 ; python_version >= "3.8" and python_version < "4.0" \
-    --hash=sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd \
-    --hash=sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5
-colorama==0.4.6 ; python_version >= "3.8" and python_version < "4.0" and platform_system == "Windows" \
+click==8.1.7 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
+    --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
+colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" and platform_system == "Windows" \
     --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
     --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
-flask-compress==1.13 ; python_version >= "3.8" and python_version < "4.0" \
+flask-compress==1.13 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:1128f71fbd788393ce26830c51f8b5a1a7a4d085e79a21a5cddf4c057dcd559b \
     --hash=sha256:ee96f18bf9b00f2deb4e3406ca4a05093aa80e2ef0578525a3b4d32ecdff129d
-flask==2.3.2 ; python_version >= "3.8" and python_version < "4.0" \
-    --hash=sha256:77fd4e1249d8c9923de34907236b747ced06e5467ecac1a7bb7115ae0e9670b0 \
-    --hash=sha256:8c2f9abd47a9e8df7f0c3f091ce9497d011dc3b31effcf4c85a6e2b50f4114ef
-idna==3.4 ; python_version >= "3.8" and python_version < "4.0" \
+flask==2.3.3 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc \
+    --hash=sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b
+idna==3.4 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
     --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2
-importlib-metadata==6.8.0 ; python_version >= "3.8" and python_version < "4.0" \
+importlib-metadata==6.8.0 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb \
     --hash=sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743
-inquirer==3.1.3 ; python_version >= "3.8" and python_version < "4.0" \
+inquirer==3.1.3 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:a7441fd74d06fcac4385218a1f5e8703f7a113f7944e01af47b8c58e84f95ce5 \
     --hash=sha256:aac309406f5b49d4b8ab7c6872117f43bf082a552dc256aa16bc95e16bb58bec
-itsdangerous==2.1.2 ; python_version >= "3.8" and python_version < "4.0" \
+itsdangerous==2.1.2 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \
     --hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a
-jinja2==3.1.2 ; python_version >= "3.8" and python_version < "4.0" \
+jinja2==3.1.2 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \
     --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
-jinxed==1.2.0 ; python_version >= "3.8" and python_version < "4.0" and platform_system == "Windows" \
+jinxed==1.2.0 ; python_version >= "3.10" and python_version < "4.0" and platform_system == "Windows" \
     --hash=sha256:032acda92d5c57cd216033cbbd53de731e6ed50deb63eb4781336ca55f72cda5 \
     --hash=sha256:cfc2b2e4e3b4326954d546ba6d6b9a7a796ddcb0aef8d03161d005177eb0d48b
-linkify-it-py==2.0.2 ; python_version >= "3.8" and python_version < "4.0" \
+linkify-it-py==2.0.2 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:19f3060727842c254c808e99d465c80c49d2c7306788140987a1a7a29b0d6ad2 \
     --hash=sha256:a3a24428f6c96f27370d7fe61d2ac0be09017be5190d68d8658233171f1b6541
-markdown-it-py==3.0.0 ; python_version >= "3.8" and python_version < "4.0" \
+markdown-it-py==3.0.0 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \
     --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb
-markdown-it-py[linkify,plugins]==3.0.0 ; python_version >= "3.8" and python_version < "4.0" \
+markdown-it-py[linkify,plugins]==3.0.0 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \
     --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb
-markupsafe==2.1.3 ; python_version >= "3.8" and python_version < "4.0" \
+markupsafe==2.1.3 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
     --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
     --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
@@ -262,19 +265,19 @@ markupsafe==2.1.3 ; python_version >= "3.8" and python_version < "4.0" \
     --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
     --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
     --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2
-mdit-py-plugins==0.4.0 ; python_version >= "3.8" and python_version < "4.0" \
+mdit-py-plugins==0.4.0 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9 \
     --hash=sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b
-mdurl==0.1.2 ; python_version >= "3.8" and python_version < "4.0" \
+mdurl==0.1.2 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
     --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
-miko==1.1 ; python_version >= "3.8" and python_version < "4.0" \
+miko==1.1 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:72559a19ab3b50748e05cd9336b7d6b2015125558ed5e3e7ef50c91872e5c96f \
     --hash=sha256:d48a30e7619b4f58c7b921e891ed0970b50de652322d47c15a340a09b26a048b
-nasse==2.0.0 ; python_version >= "3.8" and python_version < "4.0" \
-    --hash=sha256:1a1a33000d4929b1bafb47686d7d586686d9f3d3562418e85bd8f18cfd7d6e44 \
-    --hash=sha256:54349c2dbc8f72af2083989c48b3871421cb3cf23e644011caed2e64698eff9d
-nh3==0.2.14 ; python_version >= "3.8" and python_version < "4.0" \
+nasse==2.0.2 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:4fad0406967972b62903d34121b9ba0b4abb8919d622a9f7923e921df42ae595 \
+    --hash=sha256:acec9c206e6a13409ad703d7bdda19db0a9d16251a5aa117c95f6f6f036a52b9
+nh3==0.2.14 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:116c9515937f94f0057ef50ebcbcc10600860065953ba56f14473ff706371873 \
     --hash=sha256:18415df36db9b001f71a42a3a5395db79cf23d556996090d293764436e98e8ad \
     --hash=sha256:203cac86e313cf6486704d0ec620a992c8bc164c86d3a4fd3d761dd552d839b5 \
@@ -291,47 +294,52 @@ nh3==0.2.14 ; python_version >= "3.8" and python_version < "4.0" \
     --hash=sha256:aed56a86daa43966dd790ba86d4b810b219f75b4bb737461b6886ce2bde38fd6 \
     --hash=sha256:e8986f1dd3221d1e741fda0a12eaa4a273f1d80a35e31a1ffe579e7c621d069e \
     --hash=sha256:f99212a81c62b5f22f9e7c3e347aa00491114a5647e1f13bbebd79c3e5f08d75
-pygments==2.16.1 ; python_version >= "3.8" and python_version < "4.0" \
+pygments==2.16.1 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \
     --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29
-python-editor==1.0.4 ; python_version >= "3.8" and python_version < "4.0" \
+pyperclip==1.8.2 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57
+python-editor==1.0.4 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d \
     --hash=sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b \
     --hash=sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8
-pyuseragents==1.0.5 ; python_version >= "3.8" and python_version < "4.0" \
+pyuseragents==1.0.5 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:7300024dcc04ed9ed4072d9aef7fbc18a832686b93acfb75e8835bbe0eb6aae6 \
     --hash=sha256:c63d66f5d80fc08fd31027be754824292c795e2161c2ccce989d2ce2f23ae19b
-readchar==4.0.5 ; python_version >= "3.8" and python_version < "4.0" \
+readchar==4.0.5 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:08a456c2d7c1888cde3f4688b542621b676eb38cd6cfed7eb6cb2e2905ddc826 \
     --hash=sha256:76ec784a5dd2afac3b7da8003329834cdd9824294c260027f8c8d2e4d0a78f43
-requests==2.31.0 ; python_version >= "3.8" and python_version < "4.0" \
+requests==2.31.0 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
     --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
-rich==13.5.2 ; python_version >= "3.8" and python_version < "4.0" \
+rich==13.5.2 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808 \
     --hash=sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39
-setuptools==68.0.0 ; python_version >= "3.8" and python_version < "4.0" \
-    --hash=sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f \
-    --hash=sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235
-six==1.16.0 ; python_version >= "3.8" and python_version < "4.0" \
+setuptools==68.1.2 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d \
+    --hash=sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b
+six==1.16.0 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
     --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
-soupsieve==2.4.1 ; python_version >= "3.8" and python_version < "4.0" \
+soupsieve==2.4.1 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8 \
     --hash=sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea
-textual==0.30.0 ; python_version >= "3.8" and python_version < "4.0" \
+textual-textarea==0.4.2 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:5a6376d7568e65cb893bd0e776bf90a215a3203c0631a2c6235edc1405e0b0b9 \
+    --hash=sha256:a4aecc01395e36e8107f4e6e281662b56013af49a2c614933cc0c6e59909514c
+textual==0.30.0 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:bf7045a7e9b7dc3ac589c38ce86ac31aecf0e76e8c8ce09aee474316bc2e2c03 \
     --hash=sha256:e87d587e4569236f3809d41955ed9556287dbedaca64724e1d6ad5adbb69c9c5
-typing-extensions==4.7.1 ; python_version >= "3.8" and python_version < "4.0" \
+typing-extensions==4.7.1 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36 \
     --hash=sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2
-uc-micro-py==1.0.2 ; python_version >= "3.8" and python_version < "4.0" \
+uc-micro-py==1.0.2 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:30ae2ac9c49f39ac6dce743bd187fcd2b574b16ca095fa74cd9396795c954c54 \
     --hash=sha256:8c9110c309db9d9e87302e2f4ad2c3152770930d88ab385cd544e7a7e75f3de0
-urllib3==2.0.4 ; python_version >= "3.8" and python_version < "4.0" \
+urllib3==2.0.4 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11 \
     --hash=sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4
-watchdog==3.0.0 ; python_version >= "3.8" and python_version < "4.0" \
+watchdog==3.0.0 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a \
     --hash=sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100 \
     --hash=sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8 \
@@ -359,12 +367,12 @@ watchdog==3.0.0 ; python_version >= "3.8" and python_version < "4.0" \
     --hash=sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64 \
     --hash=sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44 \
     --hash=sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33
-wcwidth==0.2.6 ; python_version >= "3.8" and python_version < "4.0" \
+wcwidth==0.2.6 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e \
     --hash=sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0
-werkzeug==2.3.6 ; python_version >= "3.8" and python_version < "4.0" \
-    --hash=sha256:935539fa1413afbb9195b24880778422ed620c0fc09670945185cce4d91a8890 \
-    --hash=sha256:98c774df2f91b05550078891dee5f0eb0cb797a522c757a2452b9cee5b202330
-zipp==3.16.2 ; python_version >= "3.8" and python_version < "4.0" \
+werkzeug==2.3.7 ; python_version >= "3.10" and python_version < "4.0" \
+    --hash=sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8 \
+    --hash=sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528
+zipp==3.16.2 ; python_version >= "3.10" and python_version < "4.0" \
     --hash=sha256:679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0 \
     --hash=sha256:ebc15946aa78bd63458992fc81ec3b6f7b1e92d51c35e6de1c3804e73b799147
diff --git a/translatepy/__init__.py b/translatepy/__init__.py
index f7bd81c..8481c8c 100644
--- a/translatepy/__init__.py
+++ b/translatepy/__init__.py
@@ -44,6 +44,7 @@
 ]
 
 # Imports
+from translatepy.utils import importer
 from .__info__ import __author__, __copyright__, __license__, __version__
 from translatepy.language import Language
 from translatepy.translate import Translate
@@ -73,6 +74,5 @@
 Translator = Translate
 
 # Languages
-
 AUTOMATIC = Language("auto")
 ENGLISH = Language("eng")
diff --git a/translatepy/translate.py b/translatepy/translate.py
index 1644dd9..c70132b 100644
--- a/translatepy/translate.py
+++ b/translatepy/translate.py
@@ -3,152 +3,21 @@
 
 © Anime no Sekai — 2023
 """
-import typing
-from threading import Thread
 
-from translatepy import exceptions, models
-from translatepy.language import Language
 from translatepy.translators import (PONS, QCRI, BaseTranslator, Bing, DeepL,
                                      Google, Libre, Microsoft, MyMemory,
                                      Reverso, TranslateCom, Yandex, Papago)
-from translatepy.translators.base import BaseTranslator, C
-from translatepy.utils import importer, queue, request
+import typing
+from translatepy.utils import request
+from translatepy.translators.base_aggregator import BaseTranslatorAggregator
 
 
-class Translate(BaseTranslator):
+class Translate(BaseTranslatorAggregator):
     """
     A class which groups all of the translators
     """
-
-    def __init__(self, services_list: typing.Optional[typing.List[typing.Union[BaseTranslator, typing.Type[BaseTranslator]]]] = None,
-                 session: typing.Optional[request.Session] = None, fast: bool = False):
-        """
-        A special `Translator` class which groups multiple translators to have better results.
-
-        Parameters
-        ----------
-        services_list: list[Type | BaseTranslator]
-            A list of instanciated `BaseTranslator` or subclasses of `BaseTranslator` to use as translators
-        session: utils.request.Session
-            The `Session` object used to make all of the requests
-        fast: bool
-            Enables fast mode (concurrent processing)
-        """
-        super().__init__(session)
-        self.services_list = services_list or [Google, Yandex, Microsoft, Reverso, Bing,
+    def __init__(self, services_list: typing.Optional[typing.List[typing.Union[BaseTranslator, typing.Type[BaseTranslator]]]] = None, session: typing.Optional[request.Session] = None, fast: bool = False):
+        services_list = services_list or [Google, Yandex, Microsoft, Reverso, Bing,
                                                DeepL, Libre, TranslateCom, MyMemory, PONS, QCRI, Papago]
 
-        try:
-            _ = iter(self.services_list)
-        except Exception as err:
-            raise exceptions.ParameterTypeError("Parameter 'services_list' must be iterable, {} was given".format(type(services_list).__name__)) from err
-
-        self.FAST_MODE = fast
-
-        if isinstance(session, type):  # is not instantiated
-            self.session = session()
-        else:
-            self.session = session
-
-        self.services = []
-        for service in self.services_list:
-            if isinstance(service, str):
-                service = importer.get_translator(service)
-            if not isinstance(service, BaseTranslator):  # not instantiated
-                if not issubclass(service, BaseTranslator):
-                    raise exceptions.ParameterTypeError("{service} must be a child class of the BaseTranslator class".format(service=service))
-                # avoiding to instantiate anything because it might not be used
-            self.services.append(service)
-
-    def _instantiate_translator(self,
-                                service: typing.Union[str, BaseTranslator, typing.Type[BaseTranslator]],
-                                services_list: list, index: int) -> BaseTranslator:
-        """Internal function to instantiate a translator"""
-        if isinstance(service, str):
-            service = importer.get_translator(service)
-        if not isinstance(service, BaseTranslator):  # not instantiated
-            try:
-                service = service(session=self.session)  # it should want `session` because that's how `BaseTranslator` is implemented
-            except TypeError:
-                service = service()
-            services_list[index] = service
-        return service
-
-    def _apply(self, work: str, *args, **kwargs):
-        """
-        Tests `work` on all of the services
-
-        Parameters
-        ----------
-        work: str
-            The function name to call
-        *args
-        **kwargs
-            The arguments to give to `work`
-        """
-
-        def worker(translator: BaseTranslator, index: int):
-            translator = self._instantiate_translator(translator, self.services, index)
-            try:
-                result = getattr(translator, work)(*args, **kwargs)
-            except Exception:
-                raise exceptions.NoResult("{service} did not return any value".format(service=translator.__repr__()))
-            if not result:
-                pass
-            return result
-
-        def fast_work(queue: queue.Queue, translator: BaseTranslator, index: int):
-            try:
-                queue.put(worker(translator=translator, index=index))
-            except Exception:
-                pass
-
-        if self.FAST_MODE:
-            _queue = queue.Queue()
-            threads = []
-            for index, service in enumerate(self.services):
-                thread = Thread(target=fast_work, args=(_queue, service, index))
-                thread.start()
-                threads.append(thread)
-            result = _queue.get(threads=threads)  # wait for a value and return it
-            if result is None:
-                raise exceptions.NoResult("No service has returned a valid result")
-            return result
-
-        for index, service in enumerate(self.services):
-            try:
-                return worker(service, index=index)
-            except Exception:
-                continue
-
-        raise exceptions.NoResult("No service has returned a valid result")
-
-    def _translate(self: C, text: str, dest_lang: typing.Any, source_lang: typing.Any, *args, **kwargs) -> models.TranslationResult[C]:
-        return self._apply("translate", text=text, dest_lang=dest_lang, source_lang=source_lang, *args, **kwargs)
-
-    def _alternatives(self: C, translation: models.TranslationResult, *args, **kwargs) -> typing.Union[models.TranslationResult[C], typing.List[models.TranslationResult[C]]]:
-        return self._apply("alternatives", translation=translation, *args, **kwargs)
-
-    def _transliterate(self: C, text: str, dest_lang: typing.Any, source_lang: typing.Any, *args, **kwargs) -> models.TransliterationResult[C]:
-        return self._apply("transliterate", text=text, dest_lang=dest_lang, source_lang=source_lang, *args, **kwargs)
-
-    def _spellcheck(self: C, text: str, source_lang: typing.Any, *args, **kwargs) -> typing.Union[models.SpellcheckResult[C], models.RichSpellcheckResult[C]]:
-        return self._apply("spellcheck", text=text, source_lang=source_lang, *args, **kwargs)
-
-    def _language(self: C, text: str, *args, **kwargs) -> models.LanguageResult[C]:
-        return self._apply("language", text=text, *args, **kwargs)
-
-    def _example(self: C, text: str, source_lang: typing.Any, *args, **kwargs) -> typing.Union[models.ExampleResult[C], typing.List[models.ExampleResult[C]]]:
-        return self._apply("example", text=text, source_lang=source_lang, *args, **kwargs)
-
-    def _dictionary(self: C, text: str, source_lang: typing.Any, *args, **kwargs) -> typing.Union[typing.Union[models.DictionaryResult[C], models.RichDictionaryResult[C]], typing.List[typing.Union[models.DictionaryResult[C], models.RichDictionaryResult[C]]]]:
-        return self._apply("dictionary", text=text, source_lang=source_lang, *args, **kwargs)
-
-    def _text_to_speech(self: C, text: str, speed: int, gender: models.Gender, source_lang: typing.Any, *args, **kwargs) -> models.TextToSpeechResult[C]:
-        return self._apply("text_to_speech", text=text, speed=speed, gender=gender, source_lang=source_lang, *args, **kwargs)
-
-    def _code_to_language(self, code: Language) -> Language:
-        return code
-
-    def _language_to_code(self, language: Language) -> Language:
-        return language
+        super().__init__(services_list, session, fast)
diff --git a/translatepy/translators/base.py b/translatepy/translators/base.py
index 867a1b3..b6c6b64 100644
--- a/translatepy/translators/base.py
+++ b/translatepy/translators/base.py
@@ -7,18 +7,19 @@
 # This shouldn't be a problem if we are targetting sub-3.6 python versions
 # pylint: disable=consider-using-f-string
 # pylint: disable=line-too-long
+import typing
+
+from translatepy import exceptions, models
+from translatepy.language import Language
 
 import enum
-import typing
 import functools
 import collections
 import multiprocessing.pool
 
 import bs4
 
-from translatepy import models, exceptions
 from translatepy.utils import hasher, lru, sanitize, request
-from translatepy.language import Language
 
 # Types
 C = typing.TypeVar('C', bound="BaseTranslator")  # The expected result class for `models.Result`
@@ -161,7 +162,7 @@ def _language_to_code(self, language: Language) -> typing.Union[str, typing.Any]
         Any
             The language code to be used internally
         """
-        return self._validate_language(language).alpha2
+        return language.alpha2
 
     def _code_to_language(self, code: typing.Union[str, typing.Any]) -> Language:
         """
@@ -192,6 +193,9 @@ def _validate_language(self, language: typing.Union[str, Language], *args, **kwa
         str
         """
         language = Language(language, *args, **kwargs)
+        if not language:
+            raise ValueError("The given language is not valid")
+
         language_code = self._language_to_code(language)
 
         if self._supported_languages:  # Check if the attribute is not empty
diff --git a/translatepy/translators/base_aggregator.py b/translatepy/translators/base_aggregator.py
new file mode 100644
index 0000000..cd9a6e1
--- /dev/null
+++ b/translatepy/translators/base_aggregator.py
@@ -0,0 +1,145 @@
+from translatepy.translators.base import BaseTranslator, C
+from translatepy.language import Language
+from translatepy import exceptions, models
+import typing
+from threading import Thread
+from translatepy.utils import importer, queue, request
+
+
+class BaseTranslatorAggregator(BaseTranslator):
+    def __init__(self, services_list: typing.Optional[typing.List[typing.Union[BaseTranslator, typing.Type[BaseTranslator]]]] = None,
+                 session: typing.Optional[request.Session] = None, fast: bool = False):
+        """
+        A special `Translator` class which groups multiple translators to have better results.
+
+        Parameters
+        ----------
+        services_list: list[Type | BaseTranslator]
+            A list of instanciated `BaseTranslator` or subclasses of `BaseTranslator` to use as translators
+        session: utils.request.Session
+            The `Session` object used to make all of the requests
+        fast: bool
+            Enables fast mode (concurrent processing)
+        """
+
+        super().__init__(session)
+
+        if not services_list:
+            raise exceptions.ParameterTypeError("Parameter 'services_list' cannot be empty")
+
+        self.services_list = services_list
+
+        try:
+            _ = iter(self.services_list)
+        except Exception as err:
+            raise exceptions.ParameterTypeError("Parameter 'services_list' must be iterable, {} was given".format(type(services_list).__name__)) from err
+
+        self.FAST_MODE = fast
+
+        if isinstance(session, type):  # is not instantiated
+            self.session = session()
+        else:
+            self.session = session
+
+        self.services = []
+        for service in self.services_list:
+            if isinstance(service, str):
+                service = importer.get_translator(service)
+            if not isinstance(service, BaseTranslator):  # not instantiated
+                if not issubclass(service, BaseTranslator):
+                    raise exceptions.ParameterTypeError("{service} must be a child class of the BaseTranslator class".format(service=service))
+                # avoiding to instantiate anything because it might not be used
+            self.services.append(service)
+
+    def _instantiate_translator(self,
+                                service: typing.Union[str, BaseTranslator, typing.Type[BaseTranslator]],
+                                services_list: list, index: int) -> BaseTranslator:
+        """Internal function to instantiate a translator"""
+        if isinstance(service, str):
+            service = importer.get_translator(service)
+        if not isinstance(service, BaseTranslator):  # not instantiated
+            try:
+                service = service(session=self.session)  # it should want `session` because that's how `BaseTranslator` is implemented
+            except TypeError:
+                service = service()
+            services_list[index] = service
+        return service
+
+    def _apply(self, work: str, *args, **kwargs):
+        """
+        Tests `work` on all of the services
+
+        Parameters
+        ----------
+        work: str
+            The function name to call
+        *args
+        **kwargs
+            The arguments to give to `work`
+        """
+
+        def worker(translator: BaseTranslator, index: int):
+            translator = self._instantiate_translator(translator, self.services, index)
+            try:
+                result = getattr(translator, work)(*args, **kwargs)
+            except Exception:
+                raise exceptions.NoResult("{service} did not return any value".format(service=translator.__repr__()))
+            if not result:
+                pass
+            return result
+
+        def fast_work(queue: queue.Queue, translator: BaseTranslator, index: int):
+            try:
+                queue.put(worker(translator=translator, index=index))
+            except Exception:
+                pass
+
+        if self.FAST_MODE:
+            _queue = queue.Queue()
+            threads = []
+            for index, service in enumerate(self.services):
+                thread = Thread(target=fast_work, args=(_queue, service, index))
+                thread.start()
+                threads.append(thread)
+            result = _queue.get(threads=threads)  # wait for a value and return it
+            if result is None:
+                raise exceptions.NoResult("No service has returned a valid result")
+            return result
+
+        for index, service in enumerate(self.services):
+            try:
+                return worker(service, index=index)
+            except Exception:
+                continue
+
+        raise exceptions.NoResult("No service has returned a valid result")
+
+    def _translate(self: C, text: str, dest_lang: typing.Any, source_lang: typing.Any, *args, **kwargs) -> models.TranslationResult[C]:
+        return self._apply("translate", text=text, dest_lang=dest_lang, source_lang=source_lang, *args, **kwargs)
+
+    def _alternatives(self: C, translation: models.TranslationResult, *args, **kwargs) -> typing.Union[models.TranslationResult[C], typing.List[models.TranslationResult[C]]]:
+        return self._apply("alternatives", translation=translation, *args, **kwargs)
+
+    def _transliterate(self: C, text: str, dest_lang: typing.Any, source_lang: typing.Any, *args, **kwargs) -> models.TransliterationResult[C]:
+        return self._apply("transliterate", text=text, dest_lang=dest_lang, source_lang=source_lang, *args, **kwargs)
+
+    def _spellcheck(self: C, text: str, source_lang: typing.Any, *args, **kwargs) -> typing.Union[models.SpellcheckResult[C], models.RichSpellcheckResult[C]]:
+        return self._apply("spellcheck", text=text, source_lang=source_lang, *args, **kwargs)
+
+    def _language(self: C, text: str, *args, **kwargs) -> models.LanguageResult[C]:
+        return self._apply("language", text=text, *args, **kwargs)
+
+    def _example(self: C, text: str, source_lang: typing.Any, *args, **kwargs) -> typing.Union[models.ExampleResult[C], typing.List[models.ExampleResult[C]]]:
+        return self._apply("example", text=text, source_lang=source_lang, *args, **kwargs)
+
+    def _dictionary(self: C, text: str, source_lang: typing.Any, *args, **kwargs) -> typing.Union[typing.Union[models.DictionaryResult[C], models.RichDictionaryResult[C]], typing.List[typing.Union[models.DictionaryResult[C], models.RichDictionaryResult[C]]]]:
+        return self._apply("dictionary", text=text, source_lang=source_lang, *args, **kwargs)
+
+    def _text_to_speech(self: C, text: str, speed: int, gender: models.Gender, source_lang: typing.Any, *args, **kwargs) -> models.TextToSpeechResult[C]:
+        return self._apply("text_to_speech", text=text, speed=speed, gender=gender, source_lang=source_lang, *args, **kwargs)
+
+    def _code_to_language(self, code: Language) -> Language:
+        return code
+
+    def _language_to_code(self, language: Language) -> Language:
+        return language
diff --git a/translatepy/translators/deepl.py b/translatepy/translators/deepl.py
index 5b23a9a..c5a4c95 100644
--- a/translatepy/translators/deepl.py
+++ b/translatepy/translators/deepl.py
@@ -14,7 +14,10 @@
 
 import re
 import time
+import uuid
+import enum
 import random
+import secrets
 import typing
 
 from bs4 import BeautifulSoup
@@ -22,11 +25,22 @@
 from translatepy import models, exceptions
 from translatepy.language import Language
 from translatepy.translators.base import BaseTranslateException, BaseTranslator, C
+from translatepy.translators.base_aggregator import BaseTranslatorAggregator
 from translatepy.utils import request
 
 SENTENCES_SPLITTING_REGEX = re.compile('(?<=[.!:?]) +')
 
 
+class DeeplFormality(enum.Enum):
+    formal = "formal"
+    informal = "informal"
+
+
+class DeeplTranslate(BaseTranslatorAggregator):
+    def __init__(self, session: request.Session = None, *args, **kwargs) -> None:
+        super().__init__([DeeplTranslateV1, DeeplTranslateV2], session, *args, **kwargs)
+
+
 class DeeplTranslateException(BaseTranslateException):
     """
     Default DeepL Translate exception
@@ -82,7 +96,7 @@ def __init__(self, session: request.Session) -> None:
         self.session = session
         self.last_access = 0
 
-    def dump(self, method, params):
+    def dump(self, method: str, params: dict):
         self.id_number += 1
         data = {
             "jsonrpc": "2.0",
@@ -92,31 +106,32 @@ def dump(self, method, params):
         }
         return data
 
-    def send_jsonrpc(self, method, params):
+    def send_jsonrpc(self, method: str, params: dict) -> dict:
         # Take a break 3 sec between requests, so as not to get a block by the IP address
         if time.time() - self.last_access < 3:
             distance = 3 - (time.time() - self.last_access)
             time.sleep((distance if distance >= 0 else 0))
 
-        request = self.session.post("https://www2.deepl.com/jsonrpc", json=self.dump(method, params))
+        request = self.session.post("https://www2.deepl.com/jsonrpc", json={"method": method, "params": params}, params={"client": "chrome-extension,1.6.1"})
         self.last_access = time.time()
         response = request.json()
         if request.status_code == 200:
             return response["result"]
         else:
-            raise DeeplTranslateException(response["error"]["code"])
-
+            raise DeeplTranslateException(response["error"]["code"], response["error"]["message"])
 
-class DeeplTranslate(BaseTranslator):
 
-    _supported_languages = {'AUTO', 'BG', 'CS', 'DA', 'DE', 'EL', 'EN', 'ES', 'ET', 'FI', 'FR', 'HU', 'IT', 'JA', 'LT', 'LV', 'NL', 'PL', 'PT', 'RO', 'RU', 'SK', 'SL', 'SV', 'ZH', 'TR', 'ID', 'UK'}
+class DeeplTranslateV1(BaseTranslator):
+    _supported_languages: dict = {'AUTO', 'BG', 'CS', 'DA', 'DE', 'EL', 'EN', 'ES', 'ET', 'FI', 'FR', 'HU', 'IT', 'JA', 'LT', 'LV', 'NL', 'PL', 'PT', 'RO', 'RU', 'SK', 'SL', 'SV', 'ZH', 'TR', 'ID', 'UK'}
 
     def __init__(self, session: request.Session = None, preferred_langs: typing.List = ["EN", "RU"]) -> None:
         super().__init__(session)
-        self.jsonrpc = JSONRPCRequest(session)
+        self.session.headers["User-Agent"] = "DeepLBrowserExtension/1.6.1 Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
+        self.session.get("https://static.deepl.com/img/illustrations/pro-features-3x1.svg")  # we need to get some security cookies from Cloudflare
+        self.jsonrpc = JSONRPCRequest(self.session)
         self.user_preferred_langs = preferred_langs
 
-    def _split_into_sentences(self, text: str, dest_lang: str, source_lang: str) -> typing.Tuple[typing.List[str], str]:
+    def _split_into_sentences(self: C, text: str, dest_lang: typing.Any, source_lang: typing.Any) -> typing.Tuple[typing.List[str], str]:
         """
         Split a string into sentences using the DeepL API.\n
         Fallbacks to a simple Regex splitting if an error occurs or no result is found
@@ -132,7 +147,7 @@ def _split_into_sentences(self, text: str, dest_lang: str, source_lang: str) ->
             "texts": [text.strip()],  # What for need strip there?
             "lang": {
                 "lang_user_selected": source_lang,
-                "user_preferred_langs": typing.List(set(self.user_preferred_langs + [dest_lang]))
+                "user_preferred_langs": list(set(self.user_preferred_langs + [dest_lang]))
             }
         }
         resp = self.jsonrpc.send_jsonrpc("LMT_split_into_sentences", params)
@@ -245,7 +260,7 @@ def _dictionary(self: C, text: str, source_lang: typing.Any) -> typing.Union[typ
                         #     results["less_common"].append(element.text.replace("\n", ""))
             return source_lang, _result
 
-    def _build_jobs(self, sentences, quality=""):
+    def _build_jobs(self, sentences: typing.List, quality: typing.Optional[str] = None):
         """
         Builds a job for each sentence for DeepL
         """
@@ -273,7 +288,7 @@ def _build_jobs(self, sentences, quality=""):
                 "raw_en_context_before": before.copy(),
                 "raw_en_sentence": sentence,
             }
-            if quality != "":
+            if quality is not None:
                 job["quality"] = quality
             jobs.append(job)
 
@@ -290,4 +305,82 @@ def _code_to_language(self, code: typing.Union[str, typing.Any]) -> Language:
         return Language(code)
 
     def __str__(self) -> str:
-        return "DeepL"
+        return "DeepL Web"
+
+
+class DeeplTranslateV2(BaseTranslator):
+    _client_id: str = "f02c852d-109d-448c-a9fb-5a805000f8cb"
+    _device_name: str = secrets.token_hex(16)
+    _user_agent = f"DeepL/2.4(69) Android 11 ({_device_name};aarch64)"
+
+    def __init__(self, session: request.Session = None) -> None:
+        super().__init__(session)
+        self.session.headers["User-Agent"] = self._user_agent
+        self.id_number = (random.randint(1000, 9999) * 10000) + 1  # ? I didn't verify the range, but it's better having only DeepL not working than having Translator() crash for only one service
+
+    def _translate(self, text: str, dest_lang: typing.Any, source_lang: typing.Any, formality: typing.Optional[DeeplFormality] = None) -> models.TranslationResult:
+        timestamp = int(time.time() * 10) * 100 + 1000
+
+        trace_id = str(uuid.uuid4()).replace("-", "")
+
+        random_bytes = secrets.token_bytes(8)
+        random_hex = secrets.token_hex(len(random_bytes))
+
+        headers = {
+            "referer": "https://www.deepl.com/",
+            "traceparent": f"00-{trace_id}-{random_hex}-01",
+            "x-trace-id": trace_id,
+            "x-instance": self._client_id,
+            "client-id": self._client_id,
+            "x-app-os-name": "Android",
+            "x-app-os-version": "11",
+            "x-app-version": "2.4",
+            "x-app-build": "69",
+            "x-app-device": self._device_name,
+            "x-app-instance-id": self._client_id,
+            "accept-encoding": "gzip"
+        }
+
+        data = {
+            "params": {
+                "texts": [{"text": text, "requestAlternatives": 3}],
+                "splitting": "newlines",
+                "commonJobParams": {"wasSpoken": False, "formality": formality},
+                "lang": {"target_lang": dest_lang, "source_lang_user_selected": source_lang},
+                "timestamp": timestamp
+            },
+            "id": self.id_number,
+            "jsonrpc": "2.0",
+            "method": "LMT_handle_texts"
+        }
+
+        request = self.session.post("https://www2.deepl.com/jsonrpc", headers=headers, json=data)
+
+        if request.status_code != 200:
+            raise DeeplTranslateException(request.status_code, request.text)
+
+        response = request.json()
+
+        self.id_number += 1
+
+        source_lang = response["result"]["lang"]
+        dest_lang = list(response["result"]["detectedLanguages"])[0]
+
+        return models.TranslationResult(source_lang=source_lang, dest_lang=dest_lang, translation=response["result"]["texts"][0]["text"])
+
+    def _language_to_code(self, language: Language) -> typing.Union[str, typing.Any]:
+        if language.id == "zho":
+            return "ZH"
+        elif language.id == "auto":
+            return ""
+        return language.alpha2.upper()
+
+    def _code_to_language(self, code: typing.Union[str, typing.Any]) -> Language:
+        if str(code).lower() in {"zh", "zh-cn"}:
+            return Language("zho")
+        elif str(code) == "":
+            return Language("auto")
+        return Language(code)
+
+    def __str__(self: C) -> str:
+        return "DeepL Android API"
diff --git a/translatepy/translators/google.py b/translatepy/translators/google.py
index 21da0f8..78e8173 100644
--- a/translatepy/translators/google.py
+++ b/translatepy/translators/google.py
@@ -8,10 +8,11 @@
 import json
 import typing
 
-from translatepy import exceptions, models
-from translatepy.exceptions import ServiceURLError, UnsupportedMethod
+from translatepy import models
+from translatepy.exceptions import ServiceURLError
 from translatepy.language import Language
 from translatepy.translators.base import BaseTranslator, C
+from translatepy.translators.base_aggregator import BaseTranslatorAggregator
 from translatepy.utils import request
 from translatepy.utils.gtoken import TokenAcquirer
 from translatepy.utils.utils import convert_to_float
@@ -62,72 +63,17 @@
                        'xh', 'yi', 'yo', 'zh-CN', 'zh-TW', 'zh-cn', 'zu'}
 
 
-# For backward compatibility
-class GoogleTranslate(BaseTranslator):
-
-    _supported_languages = SUPPORTED_LANGUAGES
-
-    def __init__(self, session: typing.Optional[request.Session] = None, service_url: str = "translate.google.com"):
-        super().__init__(session)
+class GoogleTranslate(BaseTranslatorAggregator):
+    def __init__(self, session: typing.Optional[request.Session] = None, service_url: str = "translate.google.com", *args, **kwargs):
         if service_url not in DOMAINS:
             raise ServiceURLError("{url} is not a valid service URL".format(url=str(service_url)))
 
-        google_v1 = GoogleTranslateV1(service_url=service_url, session=self.session)
-        google_v2 = GoogleTranslateV2(service_url=service_url, session=self.session)
+        google_v1 = GoogleTranslateV1(service_url=service_url, session=session)
+        google_v2 = GoogleTranslateV2(service_url=service_url, session=session)
 
-        self.services = [google_v1, google_v2]
+        services_list = [google_v1, google_v2]
 
-    def _translate(self: C, text: str, dest_lang: typing.Any, source_lang: typing.Any) -> models.TranslationResult[C]:
-        for service in self.services:
-            try:
-                return service._translate(text, dest_lang, source_lang)
-            except Exception:
-                continue
-        raise ValueError("No service has returned a valid result")
-
-    def _transliterate(self: C, text: str, dest_lang: typing.Any, source_lang: typing.Any) -> models.TransliterationResult[C]:
-        for service in self.services:
-            try:
-                return service._transliterate(text, dest_lang, source_lang)
-            except Exception:
-                continue
-        raise ValueError("No service has returned a valid result")
-
-    def _language(self: C, text: str) -> models.LanguageResult[C]:
-        for service in self.services:
-            try:
-                return service._language(text)
-            except Exception:
-                continue
-        raise ValueError("No service has returned a valid result")
-
-    def _language_to_code(self, language: Language) -> typing.Union[str, typing.Any]:
-        if language.id == "zho":
-            return "zh-cn"
-        elif language.id == "och":
-            return "zh-tw"
-        return language.alpha2
-
-    def _code_to_language(self, code: typing.Union[str, typing.Any]) -> Language:
-        language_code = str(code).lower()
-        if language_code == "zh-cn":
-            return Language("zho")
-        elif language_code == "zh-tw":
-            return Language("och")
-        return Language(language_code)
-
-    # TODO: Implement `spellcheck`
-
-    def _text_to_speech(self: C, text: str, speed: int, gender: models.Gender, source_lang: typing.Any) -> models.TextToSpeechResult[C]:
-        for service in self.services:
-            try:
-                return service._text_to_speech(text, speed, gender, source_lang)
-            except Exception:
-                continue
-        raise ValueError("No service has returned a valid result")
-
-    def __str__(self):
-        return "Google"
+        super().__init__(services_list, session, *args, **kwargs)
 
 
 class GoogleTranslateV1(BaseTranslator):
diff --git a/translatepy/translators/microsoft.py b/translatepy/translators/microsoft.py
index 7e2f782..eadb5c9 100644
--- a/translatepy/translators/microsoft.py
+++ b/translatepy/translators/microsoft.py
@@ -1,209 +1,163 @@
 """
 This implementation was made specifically for translatepy by 'Zhymabek Roman'.
 """
-
-import time
+import base64
 import typing
+import json
+import hashlib
 import uuid
+import urllib
+import hmac
+import datetime as dt
 
-import cain
-import cain.types
-
-from translatepy import exceptions, models
-from translatepy.__info__ import __translatepy_dir__
-from translatepy.language import Language
-from translatepy.translators.base import (BaseTranslateException,
-                                          BaseTranslator, C)
-from translatepy.translators.bing import BingSessionManager
+from warnings import warn
+from urllib.parse import urlencode, urlparse, urlunparse
 from translatepy.utils import request
+from translatepy.language import Language
+from translatepy import models
+from translatepy.translators.base import BaseTranslator, C
+from translatepy.translators.base_aggregator import BaseTranslatorAggregator
+from translatepy.utils.request import Request
 
 
-class MicrosoftException(BaseTranslateException):
-    error_codes = {
-        429: "Too many requests"
-    }
-
+class MicrosoftTranslate(BaseTranslatorAggregator):
+    def __init__(self, session: typing.Optional[Request] = None, *args, **kwargs):
+        microsft_services = [MicrosoftTranslateV1, MicrosoftTranslateV2, MicrosoftTranslateV3]
 
-class MicrosoftSessionData(cain.types.Object):
-    region: str
-    token: str
-    token_expiration: cain.types.UInt64
+        super().__init__(microsft_services, session, *args, **kwargs)
 
 
-class MicrosoftSessionManager:
+# TODO: implement text_to_speech, maybe there is other endpoints
+class MicrosoftTranslateV1(BaseTranslator):
     """
-    Manages `Microsoft` sessions
+    A Python implementation of Microsoft Translation, reverse engenered from Microsoft Translator Android application.
+    Ported from https://github.com/d4n3436/GTranslate/blob/master/src/GTranslate/Translators/MicrosoftTranslator.cs
+    Huge thanks to d4n3436.
     """
 
-    def __init__(self, request: request.Session):
-        self.session = request
-        self.bing_session = BingSessionManager(request)
+    _api_endpoint = "dev.microsofttranslator-int.com"
+    _api_version = "3.0"
+    _api_private_key = bytes([
+        0xa2, 0x29, 0x3a, 0x3d, 0xd0, 0xdd, 0x32, 0x73,
+        0x97, 0x7a, 0x64, 0xdb, 0xc2, 0xf3, 0x27, 0xf5,
+        0xd7, 0xbf, 0x87, 0xd9, 0x45, 0x9d, 0xf0, 0x5a,
+        0x09, 0x66, 0xc6, 0x30, 0xc6, 0x6a, 0xaa, 0x84,
+        0x9a, 0x41, 0xaa, 0x94, 0x3a, 0xa8, 0xd5, 0x1a,
+        0x6e, 0x4d, 0xaa, 0xc9, 0xa3, 0x70, 0x12, 0x35,
+        0xc7, 0xeb, 0x12, 0xf6, 0xe8, 0x23, 0x07, 0x9e,
+        0x47, 0x10, 0x95, 0x91, 0x88, 0x55, 0xd8, 0x17
+    ])
 
-        self._auth_session_file = __translatepy_dir__ / "sessions" / "microsoft.cain"
-        try:
-            with self._auth_session_file.open("rb") as file:
-                _auth_session_data = cain.load(file, MicrosoftSessionData)
-            self._region = _auth_session_data.region
-            self._token = _auth_session_data.token
-            self._token_expiration = _auth_session_data.token_expiration
-        except Exception:
-            self._token = ""
-            self._token_expiration = 0
-            self._parse_authorization_data()
-            _auth_session_data = MicrosoftSessionData({
-                "region": self._region,
-                "token": self._token,
-                "token_expiration": self._token_expiration
-            })
-            self._auth_session_file.parent.mkdir(parents=True, exist_ok=True)
-            try:
-                with self._auth_session_file.open("wb") as file:
-                    cain.dump(_auth_session_data, file, MicrosoftSessionData)
-            except Exception:
-                pass
-
-    def _parse_authorization_data(self, force: bool = False):
-        if not self._token or time.time() > self._token_expiration or force:
-            # authentication token is valid for 10 minutes
-            token_response = self.bing_session.send("https://www.bing.com/tfetspktok", data={})
-            token_status = token_response.get("statusCode", 200)
-
-            if token_status != 200:
-                raise MicrosoftException(token_status, "Error during token request from the server")
-
-            self._token, self._region = token_response.get("token"), token_response.get("region")
-            self._token_expiration = time.time() + (int(token_response.get("expiryDurationInMS", 600000)) - 1000) / 1000
-
-    def send(self, url, data, params: typing.Dict = {}):
-        """Sends a request to the API"""
-        # Try 2 times to make a request
-        for _ in range(2):
-            headers = {
-                'Authorization': 'Bearer {token}'.format(token=self._token),
-                'Content-type': 'application/json',
-                'X-ClientTraceId': str(uuid.uuid4())
-            }
-            _params = {'api-version': '3.0'}
-            _params.update(params)
-            if request_type == "POST":
-                request = self.session.post(url, params=_params, json=data, headers=headers)
-            elif request_type == "GET":
-                request = self.session.get(url, params=_params, headers=headers)
-            response = request.json()
-
-            if request.status_code != 200:
-                error = response.get("error", {})
-                if error.get("code", request.status_code) == 401000:
-                    self._parse_authorization_data(force=True)
-                    continue
-                raise MicrosoftException(status_code=error.get("code"), message=error.get("message", "Unknown"))
-
-            return response
-
-
-class MicrosoftTranslate(BaseTranslator):
-    """
-    A Python implementation of Microsoft Translation's APIs
-    """
+    def _translate(self: C, text: str, dest_lang: typing.Any, source_lang: typing.Any) -> models.TranslationResult[C]:
+        translate_url = f"{self._api_endpoint}/translate"
+        translate_params = {"api-version": self._api_version, "to": dest_lang}
 
-    _supported_languages = {'af', 'am', 'ar', 'as', 'auto', 'az', 'bg', 'bn', 'bs', 'ca', 'cs', 'cy', 'da', 'de', 'el', 'en', 'es', 'et', 'fa',
-                            'fi', 'fil', 'fj', 'fr', 'fr-ca', 'ga', 'gu', 'he', 'hi', 'hr', 'ht', 'hu', 'hy', 'id', 'is', 'it', 'iu', 'ja', 'kk',
-                            'km', 'kmr', 'kn', 'ko', 'ku', 'lo', 'lt', 'lv', 'mg', 'mi', 'ml', 'mr', 'ms', 'mt', 'mww', 'my', 'nb', 'ne', 'nl', 'or',
-                            'otq', 'pa', 'pl', 'prs', 'ps', 'pt', 'pt-pt', 'ro', 'ru', 'sk', 'sl', 'sm', 'sq', 'sr-Cyrl', 'sr-Latn', 'sv', 'sw', 'ta',
-                            'te', 'th', 'ti', 'tlh-Latn', 'tlh-Piqd', 'to', 'tr', 'ty', 'uk', 'ur', 'vi', 'yua', 'yue', 'zh-Hans', 'zh-Hant'}
+        if source_lang != "auto":
+            translate_params.update({"from": source_lang})
 
-    def __init__(self, session: typing.Optional[request.Session] = None):
-        super().__init__(session)
-        self.session_manager = MicrosoftSessionManager(self.session)
+        encoded_params = urlencode(translate_params)
+        parsed_url = urlparse(translate_url)
+
+        final_translate_url = urlunparse((parsed_url.scheme, parsed_url.netloc, parsed_url.path, parsed_url.params, encoded_params, parsed_url.fragment))
+
+        request = self.session.post(f"https://{final_translate_url}", headers={"X-MT-Signature": self._get_signature(final_translate_url)}, json=[{"text": text}])
+        response = request.json()
 
-    def _translate(self: C, text: str, dest_lang: typing.Any, source_lang: typing.Any) -> models.TranslationResult[C]:
         if source_lang == "auto":
-            source_lang = self._language_to_code(self.language(text).language)
-
-        response = self.session_manager.send("https://api.cognitive.microsofttranslator.com/translate", params={'from': source_lang, 'to': dest_lang}, data=[{"text": text}])
-        return models.TranslationResult(source_lang=source_lang, raw=response, translation=response[0]["translations"][0]["text"])
-
-    def _example(self: C, text: str, source_lang: typing.Any) -> typing.Union[models.ExampleResult[C], typing.List[models.ExampleResult[C]]]:
-        # source_lang, translation = self._translate(text, source_lan, source_lang)
-
-        response = self.session_manager.send("https://api.cognitive.microsofttranslator.com/dictionary/examples", params={'from': source_lang, 'to': source_lang}, data=[{'Text': text.lower(), 'Translation': text.lower()}])
-        results = []
-        for example in response[0]["examples"]:
-            try:
-                example = dict(example)
-                # sourceTerm or targetTerm ?
-                final = "".join([example.get("sourcePrefix", ""), example.get("sourceTerm", ""), example.get("sourceSuffix", "")])
-                results.append(models.ExampleResult(
-                    source_lang=source_lang,
-                    example=final,
-                    raw=example
-                ))
-            except Exception:
-                continue
-        return results
+            source_lang = response[0]["detectedLanguage"]["language"]
+
+        return models.TranslationResult(dest_lang=dest_lang, source_lang=source_lang, translation=response[0]["translations"][0]["text"], raw=response)
+
+    # TODO: some problem with to_script and from_script
+    def _transliterate(self: C, text: str, source_lang: typing.Any, dest_lang: typing.Any, from_script: typing.Any, to_script: typing.Any) -> models.TransliterationResult[C]:
+        warn("dest_lang is ignored")
+
+        transliterate_url = f"{self._api_endpoint}/transliterate"
+
+        if source_lang == "auto":
+            source_lang = self._language(text)
+
+        transliterate_params = {"api-version": self._api_version, "language": source_lang, "fromScript": from_script, "toScript": to_script}
+
+        encoded_params = urlencode(transliterate_params)
+        parsed_url = urlparse(transliterate_url)
+
+        final_transliterate_url = urlunparse((parsed_url.scheme, parsed_url.netloc, parsed_url.path, parsed_url.params, encoded_params, parsed_url.fragment))
+
+        request = self.session.post(f"https://{final_transliterate_url}", headers={"X-MT-Signature": self._get_signature(final_transliterate_url)}, json=[{"text": text}])
+        response = request.json()
+        return models.TransliterationResult(raw=response)
 
     def _language(self: C, text: str) -> models.LanguageResult[C]:
-        response = self.session_manager.send("https://api.cognitive.microsofttranslator.com/detect", data=[{"text": text}])
-        return models.LanguageResult(raw=response, language=response[0]["language"])
+        language_detect_url = f"{self._api_endpoint}/detect"
+        language_detect_params = {"api-version": self._api_version}
+
+        encoded_params = urlencode(language_detect_params)
+        parsed_url = urlparse(language_detect_url)
+
+        final_language_detect_url = urlunparse((parsed_url.scheme, parsed_url.netloc, parsed_url.path, parsed_url.params, encoded_params, parsed_url.fragment))
+
+        request = self.session.post(f"https://{final_language_detect_url}", headers={"X-MT-Signature": self._get_signature(final_language_detect_url)}, json=[{"text": text}])
+        response = request.json()
+
+        return models.LanguageResult(source_lang=response[0]["language"], raw=response, source=text)
+
+    def _get_signature(self: C, url: str) -> str:
+        guid = uuid.uuid4().hex
+        escaped_url = urllib.parse.quote_plus(url)
+
+        now = dt.datetime.utcnow()
+        date_str = now.strftime("%a, %d %b %Y %H:%M:%SGMT")
+
+        data_to_sign = f"MSTranslatorAndroidApp{escaped_url}{date_str}{guid}".lower().encode('utf-8')
+
+        hash_bytes = hmac.new(self._api_private_key, data_to_sign, hashlib.sha256).digest()
+        signature = f"MSTranslatorAndroidApp::{base64.b64encode(hash_bytes).decode()}::{date_str}::{guid}"
+        return signature
+
+    def _language_to_code(self: C, code: Language) -> typing.Union[str, typing.Any]:
+        language = Language(code)
+        if language.id == "zho":
+            return "zh-Hans"
+        elif language.id == "och":
+            return "zh-Hant"
+        return language.alpha2
 
-    # TODO: Implement `_transliterate``
+    def _code_to_language(self: C, code: typing.Union[str, typing.Any]) -> Language:
+        language_code = str(code).lower()
+        if language_code in {"zh-cn", "zh-hans"}:
+            return Language("zho")
+        elif language_code == "zh-tw":
+            return Language("och")
+        return Language(language_code)
+
+
+class MicrosoftTranslateV2(BaseTranslator):
+    """
+    A Python implementation of Microsoft Translation, reverse engenered from Microsoft SwiftKey by ZhymabekRoman
 
-    def _dictionary(self: C, text: str, source_lang: typing.Any) -> typing.Union[typing.Union[models.DictionaryResult[C], models.RichDictionaryResult[C]], typing.List[typing.Union[models.DictionaryResult[C], models.RichDictionaryResult[C]]]]:
-        # TODO: Need to reimplement
-        raise exceptions.UnsupportedMethod("Need to reimplement")
-        source_lang = self._language(text)
+    Also I found '/v1/languages?scope=translation' endpoint, idk maybe it can be useful
+    """
 
-        dest_lang = source_lang  # ?
+    _supported_languages = {'auto', 'af', 'sq', 'am', 'ar', 'hy', 'as', 'az', 'bn', 'bs', 'bg', 'my', 'ca', 'ca', 'zh-Hans', 'cs', 'da', 'nl', 'nl', 'en', 'et', 'fj', 'fil', 'fil', 'fi', 'fr', 'fr-ca', 'de', 'ga', 'el', 'gu', 'ht', 'ht', 'he', 'hi', 'hr', 'hu', 'is', 'iu', 'id', 'it', 'ja', 'kn', 'kk', 'km', 'ko', 'ku', 'lo', 'lv', 'lt', 'ml', 'mi', 'mr', 'ms', 'mg', 'mt', 'ne', 'nb', 'nb', 'or', 'pa', 'pa', 'fa', 'pl', 'pt', 'ps', 'ps', 'ro', 'ro', 'ro', 'ru', 'sk', 'sl', 'sm', 'es', 'es', 'sr-Cyrl', 'sw', 'sv', 'ty', 'ta', 'te', 'th', 'ti', 'tlh-Latn', 'tlh-Latn', 'to', 'tr', 'uk', 'ur', 'vi', 'cy', 'zh-Hans', 'zh-Hant', 'yue', 'prs', 'mww', 'tlh-Piqd', 'kmr', 'pt-pt', 'otq', 'sr-Cyrl', 'sr-Latn', 'yua'}
+    _auth_code = "Bearer 16318c3a-5fb1-4091-8f63-65aa993e2f1d"
 
-        response = self.session_manager.send("https://api.cognitive.microsofttranslator.com/dictionary/lookup", data=[{'text': text}], params={'from': source_lang, 'to': dest_lang})
-        _result = []
-        for _dictionary in response[0]["translations"]:
-            _dictionary_result = _dictionary["displayTarget"]
-            _result.append(_dictionary_result)
-        return source_lang, _result
+    def __init__(self: C, session: typing.Optional[request.Session] = None, *args, **kwargs):
+        super().__init__(session, *args, **kwargs)
+        self.session.headers["Authorization"] = self._auth_code
 
-    def _text_to_speech(self: C, text: str, speed: int, gender: models.Gender, source_lang: typing.Any) -> models.TextToSpeechResult[C]:
+    def _translate(self, text: str, dest_lang: typing.Any, source_lang: typing.Any) -> models.TranslationResult[C]:
         if source_lang == "auto":
-            source_lang = self._language_to_code(self.language(text).language)
-
-        if gender is not models.Gender.OTHER and gender is not models.Gender.GENDERLESS:
-            gender = models.Gender.MALE
-
-        final_gender = gender.value.capitalize()
-
-        _supported_langs_url = "https://{region}.tts.speech.microsoft.com/cognitiveservices/voices/list".format(region=self.session_manager._region)
-        _supported_langs_list = self.session_manager.send(_supported_langs_url, data={})  # FIXME: No value for `data`
-
-        # all locals list: {('zh-HK', 'zh-HK'), ('de', 'de-DE'), ('da', 'da-DK'), ('id', 'id-ID'), ('ko', 'ko-KR'), ('en', 'en-NZ'), ('el', 'el-GR'), ('ms', 'ms-MY'), ('es', 'es-AR'), ('ro', 'ro-RO'), ('pl', 'pl-PL'), ('it', 'it-IT'), ('hr', 'hr-HR'), ('pt', 'pt-PT'), ('hu', 'hu-HU'), ('sw', 'sw-KE'), ('en', 'en-GB'), ('mt', 'mt-MT'), ('tr', 'tr-TR'), ('ar', 'ar-EG'), ('fr', 'fr-CA'), ('te', 'te-IN'), ('fr', 'fr-BE'), ('en', 'en-SG'), ('zh-CN', 'zh-CN'), ('fr', 'fr-FR'), ('en', 'en-PH'), ('cs', 'cs-CZ'), ('fi', 'fi-FI'), ('zh-TW', 'zh-TW'), ('de', 'de-CH'), ('nb', 'nb-NO'), ('bg', 'bg-BG'), ('he', 'he-IL'), ('en', 'en-CA'), ('en', 'en-HK'), ('es', 'es-MX'), ('en', 'en-AU'), ('th', 'th-TH'), ('pt', 'pt-BR'), ('mr', 'mr-IN'), ('sk', 'sk-SK'), ('ru', 'ru-RU'), ('nl', 'nl-NL'), ('en', 'en-US'), ('ta', 'ta-IN'), ('hi', 'hi-IN'), ('cy', 'cy-GB'), ('ar', 'ar-SA'), ('ga', 'ga-IE'), ('nl', 'nl-BE'), ('de', 'de-AT'), ('ca', 'ca-ES'), ('uk', 'uk-UA'), ('es', 'es-CO'), ('es', 'es-ES'), ('es', 'es-US'), ('en', 'en-ZA'), ('ur', 'ur-PK'), ('sv', 'sv-SE'), ('lv', 'lv-LV'), ('lt', 'lt-LT'), ('vi', 'vi-VN'), ('et', 'et-EE'), ('en', 'en-IN'), ('en', 'en-IE'), ('ja', 'ja-JP'), ('fr', 'fr-CH'), ('gu', 'gu-IN'), ('sl', 'sl-SI')}
-        _locals = {'zh-CN': 'zh-CN', 'mr': 'mr-IN', 'en': 'en-US', 'ru': 'ru-RU', 'el': 'el-GR', 'es': 'es-CO',
-                   'id': 'id-ID', 'pt': 'pt-PT', 'ko': 'ko-KR', 'ta': 'ta-IN', 'te': 'te-IN', 'et': 'et-EE',
-                   'pl': 'pl-PL', 'it': 'it-IT', 'ms': 'ms-MY', 'mt': 'mt-MT', 'ro': 'ro-RO', 'vi': 'vi-VN',
-                   'bg': 'bg-BG', 'zh-TW': 'zh-TW', 'tr': 'tr-TR', 'de': 'de-CH', 'fr': 'fr-CH', 'nb': 'nb-NO',
-                   'nl': 'nl-BE', 'uk': 'uk-UA', 'he': 'he-IL', 'ur': 'ur-PK', 'hi': 'hi-IN', 'ja': 'ja-JP',
-                   'hr': 'hr-HR', 'sv': 'sv-SE', 'hu': 'hu-HU', 'sw': 'sw-KE', 'lt': 'lt-LT', 'sl': 'sl-SI',
-                   'fi': 'fi-FI', 'lv': 'lv-LV', 'sk': 'sk-SK', 'da': 'da-DK', 'cy': 'cy-GB', 'gu': 'gu-IN',
-                   'ga': 'ga-IE', 'th': 'th-TH', 'ar': 'ar-EG', 'ca': 'ca-ES', 'zh-HK': 'zh-HK', 'cs': 'cs-CZ'}
-
-        _source_local = _locals.get(source_lang)
-
-        for _supported_lang in _supported_langs_list:
-            if _supported_lang["Locale"] == _source_local and _supported_lang["Gender"] == final_gender:
-                voice = _supported_lang["ShortName"]
-                break
-        else:
-            for _supported_lang in _supported_langs_list:
-                if _supported_lang["Locale"] == _source_local:  # `gender` is an optional parameter
-                    voice = _supported_lang["ShortName"]
-                    break
-            else:
-                raise exceptions.NoResult("Microsoft Translate doesn't support {source_lang} for text to speech conversions".format(source_lang=source_lang))
-
-        speech_url = "https://{region}.tts.speech.microsoft.com/cognitiveservices/v1".format(region=self.session_manager._region)
-        headers = {"authorization": "Bearer {token}".format(token=self.session_manager._token), "content-type": "application/ssml+xml", "x-microsoft-outputformat": "audio-48khz-192kbitrate-mono-mp3"}
-        data = "<speak version='1.0' xml:lang='{local}'><voice xml:lang='{local}' xml:gender='{gender}' name='{voice}'><prosody rate='{speed}%'>{text}</prosody></voice></speak>".format(text=text, gender=final_gender, speed=float(speed - 100), local=_source_local, voice=voice)
-        spech_result = self.session.post(speech_url, data=data.encode('utf-8'), headers=headers)
-
-        return models.TextToSpeechResult(source_lang=source_lang, result=spech_result.content)
+            source_lang = self._language(text).source_lang
+
+        request = self.session.post("https://translate.api.swiftkey.com/v1/translate", params={'from': source_lang, 'to': dest_lang}, json=[{"text": text}])
+        response = request.json()
+        return models.TranslationResult(dest_lang=dest_lang, source_lang=source_lang, translation=response[0]["translations"][0]["text"], raw=response)
+
+    def _language(self, text: str) -> models.LanguageResult[C]:
+        request = self.session.post("https://translate.api.swiftkey.com/v1/translate", params={'to': "en"}, json=[{"text": text}])
+        response = request.json()
+        return models.LanguageResult(source_lang=response[0]["detectedLanguage"]["language"], raw=response)
 
     def _language_to_code(self, language: Language) -> typing.Union[str, typing.Any]:
         if language.id == "zho":
@@ -221,4 +175,72 @@ def _code_to_language(self, code: typing.Union[str, typing.Any]) -> Language:
         return Language(language_code)
 
     def __str__(self) -> str:
-        return "Microsoft Translate"
+        return "Microsoft Translate V2"
+
+
+class MicrosoftTranslateV3(BaseTranslator):
+    # Other methods: https://learn.microsoft.com/ru-ru/dotnet/api/microsoft.crm.unifiedservicedesk.dynamics.microsofttranslationservice.languageservice?view=dynamics-usd-3#methods
+    _supported_languages = {'auto', 'af', 'sq', 'am', 'ar', 'hy', 'as', 'az', 'bn', 'bs', 'bg', 'my', 'ca', 'ca', 'zh-Hans', 'cs', 'da', 'nl', 'nl', 'en', 'et', 'fj', 'fil', 'fil', 'fi', 'fr', 'fr-ca', 'de', 'ga', 'el', 'gu', 'ht', 'ht', 'he', 'hi', 'hr', 'hu', 'is', 'iu', 'id', 'it', 'ja', 'kn', 'kk', 'km', 'ko', 'ku', 'lo', 'lv', 'lt', 'ml', 'mi', 'mr', 'ms', 'mg', 'mt', 'ne', 'nb', 'nb', 'or', 'pa', 'pa', 'fa', 'pl', 'pt', 'ps', 'ps', 'ro', 'ro', 'ro', 'ru', 'sk', 'sl', 'sm', 'es', 'es', 'sr-Cyrl', 'sw', 'sv', 'ty', 'ta', 'te', 'th', 'ti', 'tlh-Latn', 'tlh-Latn', 'to', 'tr', 'uk', 'ur', 'vi', 'cy', 'zh-Hans', 'zh-Hant', 'yue', 'prs', 'mww', 'tlh-Piqd', 'kmr', 'pt-pt', 'otq', 'sr-Cyrl', 'sr-Latn', 'yua'}
+    _app_id_list = ["TAgiBjv8rXgUxIhcK7TTXGPrFSsjhAWfqypS5SRKQxl4*", "B97A24C0E08728B33D41E853C50D405E50E46563", "3DAEE5B978BA031557E739EE1E2A68CB1FAD5909"]
+
+    def _translate(self, text: str, dest_lang: typing.Any, source_lang: typing.Any) -> models.TranslationResult[C]:
+      for app_id in self._app_id_list:
+        text_array = json.dumps([text])
+        url = "https://api.microsofttranslator.com/v2/ajax.svc/TranslateArray"
+        params = {
+            "appId": app_id,
+            "texts": text_array,
+            "from": source_lang,
+            "to": dest_lang,
+        }
+        requests = self.session.get(url, params=params)
+
+        if requests.status_code != 200:
+            # logger.warning(f"Possible not valid app_id: {app_id}")
+            continue
+
+        # Some workaround
+        requests.encoding = 'utf-8-sig'
+
+        try:
+            response = requests.json()
+            translation = response[0]["TranslatedText"]
+        except Exception as ex:
+            translation = None
+            # logger.warning(f"Possible not valid app_id: {app_id}. Exception: {ex}")
+            continue
+
+        return models.TranslationResult(dest_lang=dest_lang, source_lang=source_lang, translation=translation, raw=response)
+      else:
+          raise ValueError("No valid app_id")
+
+    def _text_to_speech(self, text: str, speed: int, gender: models.Gender, source_lang: typing.Any) -> models.TextToSpeechResult:
+        params = {
+            'word': text,
+            'lang': source_lang,
+        }
+
+        requests = self.session.get('https://www.translatedict.com/speak.php', params=params)
+        return models.TextToSpeechResult(result=requests.content)
+
+    def _language_to_code(self, language: Language) -> typing.Union[str, typing.Any]:
+        if language.id == "zho":
+            return "zh-Hans"
+        elif language.id == "auto":
+            return ""
+        elif language.id == "och":
+            return "zh-Hant"
+        return language.alpha2
+
+    def _code_to_language(self, code: typing.Union[str, typing.Any]) -> Language:
+        language_code = str(code).lower()
+        if language_code == "":
+            return Language("auto")
+        elif language_code in {"zh-cn", "zh-hans"}:
+            return Language("zho")
+        elif language_code == "zh-tw":
+            return Language("och")
+        return Language(language_code)
+
+    def __str__(self) -> str:
+        return "Microsoft Translate V3"
diff --git a/translatepy/translators/mymemory.py b/translatepy/translators/mymemory.py
index 58e912e..5363f96 100644
--- a/translatepy/translators/mymemory.py
+++ b/translatepy/translators/mymemory.py
@@ -4,13 +4,13 @@
 translatepy's implementation of MyMemory
 """
 import typing
+from urllib.parse import urlparse
 
 from translatepy import models
 from translatepy.language import Language
 from translatepy.translators.base import (BaseTranslateException,
                                           BaseTranslator, C)
 from translatepy.utils import request
-from translatepy.utils.request import Request
 
 
 class MyMemoryException(BaseTranslateException):
@@ -45,10 +45,23 @@ def _translate(self: C, text: str, dest_lang: typing.Any, source_lang: typing.An
         return models.TranslationResult(source_lang=_detected_language, translation=result["translation"], raw=data)
 
     def _language(self: C, text: str) -> models.LanguageResult[C]:
-        request = self.session.get(self.base_url, params={"q": text, "langpair": "autodetect|en"})
-        request.raise_for_status()
-        data = request.json()
-        return models.LanguageResult(language=data["matches"][0]["source"], raw=data)
+        url = "https://mymemory.translated.net/search.php"
+        params = {
+            'q': text,
+            'lang': 'en',
+            'sl': 'Autodetect',
+            'tl': 'en-GB'
+        }
+
+        response = self.session.get(url, params=params, allow_redirects=False)
+        redirected_url = response.headers['Location']
+
+        parsed_url = urlparse(redirected_url)
+        path_parts = parsed_url.path.split('/')
+        language_path = path_parts[2]
+        detected_language = Language(language_path)
+
+        return models.LanguageResult(source_lang=detected_language.alpha2, raw=redirected_url)
 
     def _language_to_code(self, language: Language) -> typing.Union[str, typing.Any]:
         if language.id == "auto":
diff --git a/translatepy/translators/papago.py b/translatepy/translators/papago.py
index d700f33..6a3000b 100644
--- a/translatepy/translators/papago.py
+++ b/translatepy/translators/papago.py
@@ -80,7 +80,7 @@ def __init__(self, session: typing.Optional[request.Session] = None, *args, **kw
             except Exception:
                 pass
 
-    def generate_headers(self, url: str, request_id: str):
+    def generate_headers(self, url: str, request_id: str) -> dict:
         """Generates the headers for the API"""
         # https://papago.naver.com/main.5fbfac62f1cf0b8694fe.chunk.js#8345:8350
         # var t = Object(E.a)(),


Test patch:
diff --git a/test.py b/test.py
deleted file mode 100644
index 00c27b7..0000000
--- a/test.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from translatepy.translators.papago import Papago
-from rich.console import Console
-
-console = Console()
-
-try:
-    p = Papago()
-    # t = p.translate("Hello world", "Japanese")
-    # print(t.__pretty__(cli=True))
-    r = p.text_to_speech("こんにちは", source_lang="Japanese")
-    r.write_to_file(f"hello.{r.extension}")
-except Exception:
-    console.print_exception(show_locals=True)