Repository: WeblateOrg/weblate

Issue:
Add Alibaba Translate machine translate service
### Basic details

Provider: Alibaba Translate

**Description**

1. This provider offer free quota, 1 million characters per month
2. they've several services that collaborate with real translators for many languages so ofcourse MT quality might be better (or in future) than to other popular providers that get developed using public people's inputs etc.

https://www.alibabacloud.com/help/en/machine-translation
https://www.alibabacloud.com/help/en/machine-translation/latest/product-pricing-introduction

- _https://translate.alibaba.com/ (note: to avoid confuse, if you click API access on this site, it will redirect to the local alibaba translate; aliyun site, but you can use above alibabacloud (en site) for easy)_
- _https://alynx.alifanyi.com/ https://alynx.alifanyi.com/pay.html
(also it has a free plan and part of alibaba translate)_
- _https://www.zuodao.com/crowdsourcing/homePage.html
(part of alibaba translate)_


Discussion:
Is this related? https://docs.weblate.org/en/latest/admin/machine.html#youdao-zhiyun-api-machine-translation
This issue seems to be a good fit for newbie contributors. You are welcome to contribute to Weblate! Don't hesitate to ask any questions you would have while implementing this.

You can learn about how to get started in our [contributors documentation](https://docs.weblate.org/en/latest/contributing/index.html).
No. it's netease company. something like gitlab, gitea
Okay, I got confused by zoudao and youdao :-)
Update: Details for documentation 
https://docs.weblate.org/en/latest/admin/machine.html#

https://www.alibabacloud.com/help/en/machine-translation
https://www.alibabacloud.com/help/zh/machine-translation/latest/common-problems-machine-translation-language-code-list
https://www.alibabacloud.com/help/en/basics-for-beginners/latest/obtain-an-accesskey-pair
Hey can you assign this issue to me.
@hussainshaikh12  You're welcome!
Beta version released: https://store.crowdin.com/alibaba
meanwhile weblate 😴

ping @hussainshaikh12

Resolving patch:
diff --git a/weblate/machinery/alibaba.py b/weblate/machinery/alibaba.py
new file mode 100644
--- /dev/null
+++ b/weblate/machinery/alibaba.py
@@ -0,0 +1,287 @@
+# Copyright © Michal Čihař <michal@weblate.org>
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+
+import json
+
+from aliyunsdkalimt.request.v20181012 import TranslateGeneralRequest
+from aliyunsdkcore.client import AcsClient
+from django.utils.functional import cached_property
+
+from .base import MachineTranslation, MachineTranslationError
+from .forms import AlibabaMachineryForm
+
+
+class AlibabaTranslation(MachineTranslation):
+    """Alibaba API machine translation support."""
+
+    name = "Alibaba"
+    max_score = 80
+
+    language_map = {
+        "zh_Hans": "zh",
+        "zh_Hant": "zh-tw",
+    }
+
+    settings_form = AlibabaMachineryForm
+
+    @cached_property
+    def client(self):
+        return AcsClient(
+            ak=self.settings["key"],
+            secret=self.settings["secret"],
+            region_id=self.settings["region"],
+        )
+
+    def download_languages(self):
+        """List of supported languages."""
+        return [
+            "ab",
+            "sq",
+            "ak",
+            "ar",
+            "an",
+            "am",
+            "as",
+            "az",
+            "ast",
+            "nch",
+            "ee",
+            "ay",
+            "ga",
+            "et",
+            "oj",
+            "oc",
+            "or",
+            "om",
+            "os",
+            "tpi",
+            "ba",
+            "eu",
+            "be",
+            "ber",
+            "bm",
+            "pag",
+            "bg",
+            "se",
+            "bem",
+            "byn",
+            "bi",
+            "bal",
+            "is",
+            "pl",
+            "bs",
+            "fa",
+            "bho",
+            "br",
+            "ch",
+            "cbk",
+            "cv",
+            "ts",
+            "tt",
+            "da",
+            "shn",
+            "tet",
+            "de",
+            "nds",
+            "sco",
+            "dv",
+            "kdx",
+            "dtp",
+            "ru",
+            "fo",
+            "fr",
+            "sa",
+            "fil",
+            "fj",
+            "fi",
+            "fur",
+            "fvr",
+            "kg",
+            "km",
+            "ngu",
+            "kl",
+            "ka",
+            "gos",
+            "gu",
+            "gn",
+            "kk",
+            "ht",
+            "ko",
+            "ha",
+            "nl",
+            "cnr",
+            "hup",
+            "gil",
+            "rn",
+            "quc",
+            "ky",
+            "gl",
+            "ca",
+            "cs",
+            "kab",
+            "kn",
+            "kr",
+            "csb",
+            "kha",
+            "kw",
+            "xh",
+            "co",
+            "mus",
+            "crh",
+            "tlh",
+            "hbs",
+            "qu",
+            "ks",
+            "ku",
+            "la",
+            "ltg",
+            "lv",
+            "lo",
+            "lt",
+            "li",
+            "ln",
+            "lg",
+            "lb",
+            "rue",
+            "rw",
+            "ro",
+            "rm",
+            "rom",
+            "jbo",
+            "mg",
+            "gv",
+            "mt",
+            "mr",
+            "ml",
+            "ms",
+            "chm",
+            "mk",
+            "mh",
+            "kek",
+            "mai",
+            "mfe",
+            "mi",
+            "mn",
+            "bn",
+            "my",
+            "hmn",
+            "umb",
+            "nv",
+            "af",
+            "ne",
+            "niu",
+            "no",
+            "pmn",
+            "pap",
+            "pa",
+            "pt",
+            "ps",
+            "ny",
+            "tw",
+            "chr",
+            "ja",
+            "sv",
+            "sm",
+            "sg",
+            "si",
+            "hsb",
+            "eo",
+            "sl",
+            "sw",
+            "so",
+            "sk",
+            "tl",
+            "tg",
+            "ty",
+            "te",
+            "ta",
+            "th",
+            "to",
+            "toi",
+            "ti",
+            "tvl",
+            "tyv",
+            "tr",
+            "tk",
+            "wa",
+            "war",
+            "cy",
+            "ve",
+            "vo",
+            "wo",
+            "udm",
+            "ur",
+            "uz",
+            "es",
+            "ie",
+            "fy",
+            "szl",
+            "he",
+            "hil",
+            "haw",
+            "el",
+            "lfn",
+            "sd",
+            "hu",
+            "sn",
+            "ceb",
+            "syr",
+            "su",
+            "hy",
+            "ace",
+            "iba",
+            "ig",
+            "io",
+            "ilo",
+            "iu",
+            "it",
+            "yi",
+            "ia",
+            "hi",
+            "id",
+            "inh",
+            "en",
+            "yo",
+            "vi",
+            "zza",
+            "jv",
+            "zh",
+            "zh-tw",
+            "yue",
+            "zu",
+        ]
+
+    def download_translations(
+        self,
+        source,
+        language,
+        text: str,
+        unit,
+        user,
+        threshold: int = 75,
+    ):
+        """Download list of possible translations from a service."""
+        # Create an API request and set the request parameters.
+        request = TranslateGeneralRequest.TranslateGeneralRequest()
+        request.set_SourceLanguage(source)  # source language
+        request.set_SourceText(text)  # original
+        request.set_TargetLanguage(language)
+        request.set_FormatType("text")
+        request.set_method("POST")
+
+        # Initiate the API request and obtain the response.
+        response = self.client.do_action_with_exception(request)
+        payload = json.loads(response)
+        if "Message" in payload:
+            raise MachineTranslationError(
+                f"Error {payload['Code']}: {payload['Message']}"
+            )
+
+        yield {
+            "text": payload["Data"]["Translated"],
+            "quality": self.max_score,
+            "service": self.name,
+            "source": text,
+        }
diff --git a/weblate/machinery/forms.py b/weblate/machinery/forms.py
--- a/weblate/machinery/forms.py
+++ b/weblate/machinery/forms.py
@@ -224,6 +224,18 @@ class AWSMachineryForm(KeySecretMachineryForm):
     )
 
 
+class AlibabaMachineryForm(KeySecretMachineryForm):
+    key = forms.CharField(
+        label=pgettext_lazy("Alibaba Translate configuration", "Access key ID")
+    )
+    secret = forms.CharField(
+        label=pgettext_lazy("Alibaba Translate configuration", "Access key secret")
+    )
+    region = forms.CharField(
+        label=pgettext_lazy("Alibaba Translate configuration", "Region ID")
+    )
+
+
 class ModernMTMachineryForm(KeyURLMachineryForm):
     url = forms.URLField(
         label=pgettext_lazy("Automatic suggestion service configuration", "API URL"),
diff --git a/weblate/machinery/models.py b/weblate/machinery/models.py
--- a/weblate/machinery/models.py
+++ b/weblate/machinery/models.py
@@ -16,6 +16,7 @@ class WeblateConf(AppConf):
     WEBLATE_MACHINERY = (
         "weblate.machinery.apertium.ApertiumAPYTranslation",
         "weblate.machinery.aws.AWSTranslation",
+        "weblate.machinery.alibaba.AlibabaTranslation",
         "weblate.machinery.baidu.BaiduTranslation",
         "weblate.machinery.deepl.DeepLTranslation",
         "weblate.machinery.glosbe.GlosbeTranslation",


Test patch:
diff --git a/weblate/machinery/tests.py b/weblate/machinery/tests.py
--- a/weblate/machinery/tests.py
+++ b/weblate/machinery/tests.py
@@ -13,6 +13,7 @@
 import httpx
 import responses
 import respx
+from aliyunsdkcore.client import AcsClient
 from botocore.stub import ANY, Stubber
 from django.core.management import call_command
 from django.core.management.base import CommandError
@@ -28,6 +29,7 @@
 from weblate.checks.tests.test_checks import MockUnit
 from weblate.configuration.models import Setting
 from weblate.lang.models import Language
+from weblate.machinery.alibaba import AlibabaTranslation
 from weblate.machinery.apertium import ApertiumAPYTranslation
 from weblate.machinery.aws import AWSTranslation
 from weblate.machinery.baidu import BAIDU_API, BaiduTranslation
@@ -1328,6 +1330,40 @@ def test_batch(self, machine=None):
             super().test_batch(machine=machine)
 
 
+class AlibabaTranslationTest(BaseMachineTranslationTest):
+    MACHINE_CLS = AlibabaTranslation
+    EXPECTED_LEN = 1
+    NOTSUPPORTED = "tog"
+    CONFIGURATION = {
+        "key": "key",
+        "secret": "secret",
+        "region": "cn-hangzhou",
+    }
+
+    def mock_empty(self):
+        raise SkipTest("Not tested")
+
+    def mock_error(self):
+        raise SkipTest("Not tested")
+
+    def mock_response(self):
+        patcher = patch.object(
+            AcsClient,
+            "do_action_with_exception",
+            Mock(
+                return_value=json.dumps(
+                    {
+                        "RequestId": "14E447CA-B93B-4526-ACD7-42AE13CC2AF6",
+                        "Data": {"Translated": "Hello"},
+                        "Code": 200,
+                    }
+                )
+            ),
+        )
+        patcher.start()
+        self.addCleanup(patcher.stop)
+
+
 class IBMTranslationTest(BaseMachineTranslationTest):
     MACHINE_CLS = IBMTranslation
     EXPECTED_LEN = 1