From 76902668a38d3efe090fb242c658e4d41053b6a8 Mon Sep 17 00:00:00 2001 From: yihuang Date: Sun, 28 Oct 2018 19:40:46 +0800 Subject: [PATCH 1/3] support baidu translator api --- docs/settings.rst | 1 + rosetta/conf/settings.py | 6 +- rosetta/templates/rosetta/js/rosetta.js | 30 +++++++++- rosetta/urls.py | 12 +++- rosetta/views.py | 77 ++++++++++++++++++++++++- 5 files changed, 120 insertions(+), 6 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index bdef1a25..7e01e7ad 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -7,6 +7,7 @@ Rosetta can be configured via the following parameters, to be defined in your pr * ``ROSETTA_ENABLE_TRANSLATION_SUGGESTIONS``: Enable AJAX translation suggestions. Defaults to ``False``. * ``YANDEX_TRANSLATE_KEY``: Translation suggestions from Yandex `Yandex.Translate API `_. To use this service you must first `obtain an AppID key `_, then specify the key here. Defaults to ``None``. * ``AZURE_CLIENT_SECRET``: Translation suggestions using the Microsoft Azure Translator API. To use this service, you must first `register for the service `_, and set ``AZURE_CLIENT_SECRET`` to either of the keys listed for your subscription. Defaults to ``None``. +* ``BAIDU_FANYI_APPID`` and ``BAIDU_FANYI_SECRETKEY``: Translation suggestions from `Baidu Translator API `_. Defaults to ``None``. * ``ROSETTA_MESSAGES_SOURCE_LANGUAGE_CODE`` and ``ROSETTA_MESSAGES_SOURCE_LANGUAGE_NAME``: Change these if the source language in your PO files isn't English. Default to ``'en'`` and ``'English'`` respectively. * ``ROSETTA_WSGI_AUTO_RELOAD`` and ``ROSETTA_UWSGI_AUTO_RELOAD``: When running WSGI daemon mode, using ``mod_wsgi`` 2.0c5 or later, this setting controls whether the contents of the gettext catalog files should be automatically reloaded by the WSGI processes each time they are modified. For performance reasons, this setting should be disabled in production environments. Default to ``False``. * ``ROSETTA_EXCLUDED_APPLICATIONS``: Exclude applications defined in this list from being translated. Defaults to ``()``. diff --git a/rosetta/conf/settings.py b/rosetta/conf/settings.py index 724b09c5..0da2afac 100644 --- a/rosetta/conf/settings.py +++ b/rosetta/conf/settings.py @@ -4,7 +4,7 @@ MESSAGES_PER_PAGE = getattr(settings, 'ROSETTA_MESSAGES_PER_PAGE', 10) -# Enable Google translation suggestions +# Enable translation suggestions by different translation services. ENABLE_TRANSLATION_SUGGESTIONS = getattr(settings, 'ROSETTA_ENABLE_TRANSLATION_SUGGESTIONS', False) @@ -15,6 +15,10 @@ # https://docs.microsoft.com/en-us/azure/cognitive-services/Translator/translator-text-how-to-signup AZURE_CLIENT_SECRET = getattr(settings, 'AZURE_CLIENT_SECRET', None) +# Can be obtained for free here: https://fanyi-api.baidu.com +BAIDU_FANYI_APPID = getattr(settings, 'BAIDU_FANYI_APPID', None) +BAIDU_FANYI_SECRETKEY = getattr(settings, 'BAIDU_FANYI_SECRETKEY', None) + # Displays this language beside the original MSGID in the admin MAIN_LANGUAGE = getattr(settings, 'ROSETTA_MAIN_LANGUAGE', None) diff --git a/rosetta/templates/rosetta/js/rosetta.js b/rosetta/templates/rosetta/js/rosetta.js index b6f00a61..39fdee88 100644 --- a/rosetta/templates/rosetta/js/rosetta.js +++ b/rosetta/templates/rosetta/js/rosetta.js @@ -21,7 +21,35 @@ google.setOnLoadCallback(function() { orig = unescape(orig).replace(//g,'\n').replace(//,'').replace(/<\/code>/g,'').replace(/>/g,'>').replace(/</g,'<'); a.attr('class','suggesting').html('...'); - $.getJSON("{% url 'rosetta.translate_text' %}", { + $.getJSON("{% url 'rosetta.translate_text_azure' %}", { + from: sourceLang, + to: destLang, + text: orig + }, + function(data) { + if (data.success){ + trans.val(unescape(data.translation).replace(/'/g,'\'').replace(/"/g,'"').replace(/%\s+(\([^\)]+\))\s*s/g,' %$1s ')); + a.hide(); + } else { + a.text(data.error); + } + } + ); + }); + {% elif rosetta_settings.BAIDU_FANYI_APPID %} + $('a.suggest').click(function(e){ + e.preventDefault(); + var a = $(this); + var str = a.html(); + var orig = $('.original .message', a.parents('tr')).html(); + var trans=$('textarea',a.parent()); + var sourceLang = '{{ rosetta_settings.MESSAGES_SOURCE_LANGUAGE_CODE }}'; + var destLang = '{{ rosetta_i18n_lang_code }}'; + + orig = unescape(orig).replace(//g,'\n').replace(//,'').replace(/<\/code>/g,'').replace(/>/g,'>').replace(/</g,'<'); + a.attr('class','suggesting').html('...'); + + $.getJSON("{% url 'rosetta.translate_text_baidu' %}", { from: sourceLang, to: destLang, text: orig diff --git a/rosetta/urls.py b/rosetta/urls.py index 6b2ca395..2977b2d7 100644 --- a/rosetta/urls.py +++ b/rosetta/urls.py @@ -38,8 +38,14 @@ name='rosetta-download-file', ), - url(r'^translate/$', - views.translate_text, - name='rosetta.translate_text', + url(r'^translate/azure/$', + views.translate_text_azure, + name='rosetta.translate_text_azure', ), + + url(r'^translate/baidu/$', + views.translate_text_baidu, + name='rosetta.translate_text_baidu', + ), + ] diff --git a/rosetta/views.py b/rosetta/views.py index 4d0593d8..ccc67eae 100644 --- a/rosetta/views.py +++ b/rosetta/views.py @@ -2,6 +2,7 @@ import os import os.path import re +import random try: # Python 3 from urllib.parse import urlencode @@ -658,7 +659,7 @@ def get(self, request, *args, **kwargs): @user_passes_test(lambda user: can_translate(user), rosetta_settings.LOGIN_URL) -def translate_text(request): +def translate_text_azure(request): def translate(text, from_language, to_language, subscription_key): """ @@ -749,5 +750,79 @@ def translate(text, from_language, to_language, subscription_key): return JsonResponse(data) +BAIDU_LANGUAGE_MAP = { + 'zh-hans': 'zh', + 'zh-hant': 'cht', + 'en': 'en', + 'ja': 'jp', + 'ko': 'kor', + 'fr': 'fra', + 'es': 'spa', + 'ar': 'ara', + 'bg': 'bul', + 'et': 'est', + 'da': 'dan', + 'fi': 'fin', + 'ro': 'rom', + 'sl': 'slo', + 'sv': 'swe', + 'vi': 'vie', +} + + +def baidu_translate(appid, secretkey, q, fromlang, tolang): + baseurl = 'http://api.fanyi.baidu.com/api/trans/vip/translate' + salt = random.randint(32768, 65536) + sign = appid+q+str(salt)+secretkey + sign = hashlib.md5(sign.encode('utf-8')).hexdigest() + url = baseurl + '?' + urlencode({ + 'appid': appid, + 'q': q, + 'from': BAIDU_LANGUAGE_MAP.get(fromlang, fromlang), + 'to': BAIDU_LANGUAGE_MAP.get(tolang, tolang), + 'salt': str(salt), + 'sign': sign + }) + try: + print('url', url) + response = requests.get(url).text + except requests.exceptions.RequestException as err: + data = { + 'success': False, + 'error': "Error connecting to Baidu Translation Service: {0}".format(err) + } + else: + rsp = json.loads(response) + if 'error_code' in rsp: + data = { + 'success': False, + 'error': 'Baidu translation service error: {0} {1}'.format( + rsp['error_code'], rsp['error_msg'] + ) + } + elif not rsp.get('trans_result'): + data = { + 'success': False, + 'error': 'Baidu translation service don\'t return translation result' + } + else: + data = { + 'success': True, + 'translation': rsp['trans_result'][0]['dst'] + } + + return data + + +@user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL) +def translate_text_baidu(request): + appid = getattr(settings, 'BAIDU_FANYI_APPID', None) + secretkey = getattr(settings, 'BAIDU_FANYI_SECRETKEY', None) + if not appid or not secretkey: + return JsonResponse(data={'success': False, 'error': 'Baidu fanyi service is not configed'}) + return JsonResponse(baidu_translate( + appid, secretkey, request.GET['text'], request.GET['from'], request.GET['to'])) + + def urlencode_safe(query): return urlencode({k: force_bytes(v) for k, v in query.items()}) From 1e7cbba2257fe2e03b2d84678262b4d5b5e5d512 Mon Sep 17 00:00:00 2001 From: yihuang Date: Sat, 17 Nov 2018 11:47:35 +0800 Subject: [PATCH 2/3] fix tests --- rosetta/tests/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosetta/tests/tests.py b/rosetta/tests/tests.py index deede056..3e9d7365 100644 --- a/rosetta/tests/tests.py +++ b/rosetta/tests/tests.py @@ -983,7 +983,7 @@ def test_46_search_string_with_unicode_symbols(self): @vcr.use_cassette('fixtures/vcr_cassettes/test_47_azure_ajax_translation.yaml', match_on=['method', 'scheme', 'host', 'port', 'path', 'query', 'raw_body'], record_mode='new_episodes') def test_47_azure_ajax_translation(self): - r = self.client.get(reverse('rosetta.translate_text') + '?from=en&to=fr&text=hello%20world') + r = self.client.get(reverse('rosetta.translate_text_azure') + '?from=en&to=fr&text=hello%20world') self.assertContains(r, '"Salut tout le monde"') def test_48_requires_auth_not_respected_issue_203(self): From 1f633edcb253ed42837c857c158df18b8bbb3924 Mon Sep 17 00:00:00 2001 From: yihuang Date: Sat, 17 Nov 2018 11:51:35 +0800 Subject: [PATCH 3/3] use rosetta_settings --- rosetta/views.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rosetta/views.py b/rosetta/views.py index ccc67eae..3e4eaf03 100644 --- a/rosetta/views.py +++ b/rosetta/views.py @@ -703,10 +703,8 @@ def translate(text, from_language, to_language, subscription_key): data = {'success': True, 'translation': text} else: # run the translation: - AZURE_CLIENT_SECRET = getattr(settings, 'AZURE_CLIENT_SECRET', None) - try: - api_response = translate(text, language_from, language_to, AZURE_CLIENT_SECRET) + api_response = translate(text, language_from, language_to, rosetta_settings.AZURE_CLIENT_SECRET) # result will be a dict if there is an error, e.g. # { @@ -814,10 +812,10 @@ def baidu_translate(appid, secretkey, q, fromlang, tolang): return data -@user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL) +@user_passes_test(lambda user: can_translate(user), rosetta_settings.LOGIN_URL) def translate_text_baidu(request): - appid = getattr(settings, 'BAIDU_FANYI_APPID', None) - secretkey = getattr(settings, 'BAIDU_FANYI_SECRETKEY', None) + appid = rosetta_settings.BAIDU_FANYI_APPID + secretkey = rosetta_settings.BAIDU_FANYI_SECRETKEY if not appid or not secretkey: return JsonResponse(data={'success': False, 'error': 'Baidu fanyi service is not configed'}) return JsonResponse(baidu_translate(