From d00dca3d227db8db7c96c9ae419b3334c0c86745 Mon Sep 17 00:00:00 2001 From: Jason Moiron Date: Thu, 26 Jul 2012 14:53:52 -0400 Subject: [PATCH 01/75] force the inclusion of a timezone with timestamps that make it to the pofile to keep them compatible with pybabel --- rosetta/poutil.py | 15 +++++++++++++++ rosetta/views.py | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/rosetta/poutil.py b/rosetta/poutil.py index dc1483b..bc78af1 100644 --- a/rosetta/poutil.py +++ b/rosetta/poutil.py @@ -3,12 +3,27 @@ import django from django.conf import settings from rosetta.conf import settings as rosetta_settings from django.core.cache import cache +from django.utils import timezone +from datetime import datetime try: set except NameError: from sets import Set as set # Python 2.3 fallback +def timestamp_with_timezone(dt=None): + """ + Return a timestamp with a timezone for the configured locale. If all else + fails, consider localtime to be UTC. + """ + dt = dt or datetime.now() + if not dt.tzinfo: + tz = timezone.get_current_timezone() + if not tz: + tz = timezone.utc + dt = dt.replace(tzinfo=timezone.get_current_timezone()) + return dt.strftime("%Y-%m-%d %H:%M%z") + def find_pos(lang, project_apps=True, django_apps=False, third_party_apps=False): """ diff --git a/rosetta/views.py b/rosetta/views.py index b23238e..0fc2ab5 100644 --- a/rosetta/views.py +++ b/rosetta/views.py @@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _ from django.views.decorators.cache import never_cache from rosetta.conf import settings as rosetta_settings from rosetta.polib import pofile -from rosetta.poutil import find_pos, pagination_range +from rosetta.poutil import find_pos, pagination_range, timestamp_with_timezone from rosetta.signals import entry_changed, post_save from rosetta.storage import get_storage import re @@ -136,7 +136,7 @@ def home(request): rosetta_i18n_pofile.metadata['Last-Translator'] = unicodedata.normalize('NFKD', u"%s %s <%s>" % (request.user.first_name, request.user.last_name, request.user.email)).encode('ascii', 'ignore') rosetta_i18n_pofile.metadata['X-Translated-Using'] = u"django-rosetta %s" % rosetta.get_version(False) - rosetta_i18n_pofile.metadata['PO-Revision-Date'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M%z') + rosetta_i18n_pofile.metadata['PO-Revision-Date'] = timestamp_with_timezone() except UnicodeDecodeError: pass From 8785c39483b69195c1bf4137bf6f0bd98541b3bc Mon Sep 17 00:00:00 2001 From: David Kuchar Date: Wed, 29 Aug 2012 12:46:09 -0700 Subject: [PATCH 02/75] updated Azure Translator support --- rosetta/conf/settings.py | 3 +- rosetta/templates/rosetta/base.html | 2 +- rosetta/templates/rosetta/js/rosetta.js | 38 ++++++-------- rosetta/urls.py | 1 + rosetta/views.py | 67 ++++++++++++++++--------- 5 files changed, 63 insertions(+), 48 deletions(-) diff --git a/rosetta/conf/settings.py b/rosetta/conf/settings.py index 967afaf..8352671 100644 --- a/rosetta/conf/settings.py +++ b/rosetta/conf/settings.py @@ -7,7 +7,8 @@ MESSAGES_PER_PAGE = getattr(settings, 'ROSETTA_MESSAGES_PER_PAGE', 10) # Enable Google translation suggestions ENABLE_TRANSLATION_SUGGESTIONS = getattr(settings, 'ROSETTA_ENABLE_TRANSLATION_SUGGESTIONS', False) # Can be obtained for free here: https://ssl.bing.com/webmaster/Developers/AppIds/ -BING_APP_ID = getattr(settings, 'BING_APP_ID', None) +AZURE_CLIENT_ID = getattr(settings, 'AZURE_CLIENT_ID', None) +AZURE_CLIENT_SECRET = getattr(settings, 'AZURE_CLIENT_SECRET', 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/base.html b/rosetta/templates/rosetta/base.html index 19fe7a0..dd5de09 100644 --- a/rosetta/templates/rosetta/base.html +++ b/rosetta/templates/rosetta/base.html @@ -6,7 +6,7 @@ - + diff --git a/rosetta/templates/rosetta/js/rosetta.js b/rosetta/templates/rosetta/js/rosetta.js index 4399177..dcc9e20 100644 --- a/rosetta/templates/rosetta/js/rosetta.js +++ b/rosetta/templates/rosetta/js/rosetta.js @@ -7,7 +7,7 @@ google.setOnLoadCallback(function() { $('.hide', $(this).parent()).hide(); }); -{% if ENABLE_TRANSLATION_SUGGESTIONS and BING_APP_ID %} +{% if ENABLE_TRANSLATION_SUGGESTIONS and AZURE_CLIENT_ID and AZURE_CLIENT_SECRET %} $('a.suggest').click(function(e){ e.preventDefault(); var a = $(this); @@ -16,30 +16,24 @@ google.setOnLoadCallback(function() { var trans=$('textarea',a.parent()); var sourceLang = '{{ MESSAGES_SOURCE_LANGUAGE_CODE }}'; var destLang = '{{ rosetta_i18n_lang_code }}'; - var app_id = '{{ BING_APP_ID }}'; - var apiUrl = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate"; orig = unescape(orig).replace(//g,'\n').replace(//,'').replace(/<\/code>/g,'').replace(/>/g,'>').replace(/</g,'<'); a.attr('class','suggesting').html('...'); - window.onTranslationComplete = function(resp) { - trans.val(unescape(resp).replace(/'/g,'\'').replace(/"/g,'"').replace(/%\s+(\([^\)]+\))\s*s/g,' %$1s ')); - a.hide(); - }; - window.onTranslationError = function(response){ - a.text(response); - }; - var apiData = { - onerror: 'onTranslationError', - appid: app_id, - from: sourceLang, - to: destLang, - oncomplete: "onTranslationComplete", - text: orig - }; - $.ajax({ - url: apiUrl, - data: apiData, - dataType: 'jsonp'}); + + $.getJSON("/rosetta/translate/", { + 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); + } + } + ); }); {% endif %} diff --git a/rosetta/urls.py b/rosetta/urls.py index b701b69..181e09a 100644 --- a/rosetta/urls.py +++ b/rosetta/urls.py @@ -5,4 +5,5 @@ urlpatterns = patterns('rosetta.views', url(r'^pick/$', 'list_languages', name='rosetta-pick-file'), url(r'^download/$', 'download_file', name='rosetta-download-file'), url(r'^select/(?P[\w\-]+)/(?P\d+)/$', 'lang_sel', name='rosetta-language-selection'), + url(r'^translate/$', 'translate_text', name='translate_text'), ) diff --git a/rosetta/views.py b/rosetta/views.py index b23238e..30b668b 100644 --- a/rosetta/views.py +++ b/rosetta/views.py @@ -7,6 +7,7 @@ from django.shortcuts import render_to_response from django.template import RequestContext from django.utils.encoding import smart_unicode, iri_to_uri from django.utils.translation import ugettext_lazy as _ +from django.utils import simplejson from django.views.decorators.cache import never_cache from rosetta.conf import settings as rosetta_settings from rosetta.polib import pofile @@ -47,6 +48,7 @@ def home(request): return out_ storage = get_storage(request) + version = rosetta.get_version(True) if storage.has('rosetta_i18n_fn'): rosetta_i18n_fn = storage.get('rosetta_i18n_fn') @@ -57,11 +59,7 @@ def home(request): if rosetta_i18n_write: rosetta_i18n_pofile = pofile(rosetta_i18n_fn, wrapwidth=rosetta_settings.POFILE_WRAP_WIDTH) for entry in rosetta_i18n_pofile: - entry.md5hash = hashlib.md5( - entry.msgid.encode("utf8") + - entry.msgstr.encode("utf8") + - (entry.msgctxt and entry.msgctxt.encode("utf8") or "") - ).hexdigest() + entry.md5hash = hashlib.md5(entry.msgid.encode("utf8") + entry.msgstr.encode("utf8")).hexdigest() else: rosetta_i18n_pofile = storage.get('rosetta_i18n_pofile') @@ -172,11 +170,15 @@ def home(request): storage.set('rosetta_i18n_pofile', rosetta_i18n_pofile) # Retain query arguments - query_arg = '?_next=1' - if 'query' in request.GET or 'query' in request.POST: - query_arg += '&query=%s' % request.REQUEST.get('query') + query_arg = '' + if 'query' in request.REQUEST: + query_arg = '?query=%s' % request.REQUEST.get('query') if 'page' in request.GET: - query_arg += '&page=%d&_next=1' % int(request.GET.get('page')) + if query_arg: + query_arg = query_arg + '&' + else: + query_arg = '?' + query_arg = query_arg + 'page=%d' % int(request.GET.get('page')) return HttpResponseRedirect(reverse('rosetta-home') + iri_to_uri(query_arg)) rosetta_i18n_lang_name = _(storage.get('rosetta_i18n_lang_name')) rosetta_i18n_lang_code = storage.get('rosetta_i18n_lang_code') @@ -199,14 +201,6 @@ def home(request): page = int(request.GET.get('page')) else: page = 1 - - if '_next' in request.GET or '_next' in request.POST: - page += 1 - if page > paginator.num_pages: - page = 1 - query_arg = '?page=%d' % page - return HttpResponseRedirect(reverse('rosetta-home') + iri_to_uri(query_arg)) - rosetta_messages = paginator.page(page).object_list if rosetta_settings.MAIN_LANGUAGE and rosetta_settings.MAIN_LANGUAGE != rosetta_i18n_lang_code: @@ -235,8 +229,9 @@ def home(request): except AttributeError: ADMIN_MEDIA_PREFIX = settings.STATIC_URL + 'admin/' ADMIN_IMAGE_DIR = ADMIN_MEDIA_PREFIX + 'img/' - ENABLE_TRANSLATION_SUGGESTIONS = rosetta_settings.BING_APP_ID and rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS - BING_APP_ID = rosetta_settings.BING_APP_ID + ENABLE_TRANSLATION_SUGGESTIONS = rosetta_settings.AZURE_CLIENT_ID and rosetta_settings.AZURE_CLIENT_SECRET and rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS + AZURE_CLIENT_ID = rosetta_settings.AZURE_CLIENT_ID + AZURE_CLIENT_SECRET = rosetta_settings.AZURE_CLIENT_SECRET MESSAGES_SOURCE_LANGUAGE_NAME = rosetta_settings.MESSAGES_SOURCE_LANGUAGE_NAME MESSAGES_SOURCE_LANGUAGE_CODE = rosetta_settings.MESSAGES_SOURCE_LANGUAGE_CODE if storage.has('rosetta_last_save_error'): @@ -339,6 +334,7 @@ def lang_sel(request, langid, idx): """ Selects a file to be translated """ + storage = get_storage(request) if langid not in [l[0] for l in settings.LANGUAGES]: raise Http404 @@ -356,11 +352,7 @@ def lang_sel(request, langid, idx): storage.set('rosetta_i18n_fn', file_) po = pofile(file_) for entry in po: - entry.md5hash = hashlib.md5( - entry.msgid.encode("utf8") + - entry.msgstr.encode("utf8") + - (entry.msgctxt and entry.msgctxt.encode("utf8") or "") - ).hexdigest() + entry.md5hash = hashlib.md5(entry.msgid.encode("utf8") + entry.msgstr.encode("utf8")).hexdigest() storage.set('rosetta_i18n_pofile', po) try: @@ -389,3 +381,30 @@ def can_translate(user): except Group.DoesNotExist: return False +from microsofttranslator import Translator, TranslateApiException + +def translate_text(request): + language_from = request.GET.get('from', None) + language_to = request.GET.get('to', None) + text = request.GET.get('text', None) + + if language_from == language_to: + data = { 'success' : True, 'translation' : text } + else: + # run the translation: + AZURE_CLIENT_ID = getattr(settings, 'AZURE_CLIENT_ID', None) + AZURE_CLIENT_SECRET = getattr(settings, 'AZURE_CLIENT_SECRET', None) + + print AZURE_CLIENT_ID + print AZURE_CLIENT_SECRET + + translator = Translator(AZURE_CLIENT_ID, AZURE_CLIENT_SECRET) + + try: + translated_text = translator.translate(text, language_to) + data = { 'success' : True, 'translation' : translated_text } + except TranslateApiException as e: + data = { 'success' : False, 'error' : "Translation API Exception: {0}".format(e.message) } + + return HttpResponse(simplejson.dumps(data), mimetype='application/json') + \ No newline at end of file From 47ef9c83f6d01a3d55752d470b3299b77c14adc7 Mon Sep 17 00:00:00 2001 From: David Kuchar Date: Wed, 29 Aug 2012 12:54:04 -0700 Subject: [PATCH 03/75] added requirements.txt with microsofttranslator dependancy. --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..01b42f2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +microsofttranslator \ No newline at end of file From d959ddc3cdb99cd53da830210e52bf12a3c1c251 Mon Sep 17 00:00:00 2001 From: David Kuchar Date: Fri, 28 Sep 2012 20:02:02 -0700 Subject: [PATCH 04/75] building a separate bulk translator --- rosetta/utils.py | 42 ++++++++++++++++++++++++++++++++++++++++++ rosetta/views.py | 6 +----- 2 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 rosetta/utils.py diff --git a/rosetta/utils.py b/rosetta/utils.py new file mode 100644 index 0000000..db2ff95 --- /dev/null +++ b/rosetta/utils.py @@ -0,0 +1,42 @@ +from django.conf import settings +from django.contrib.auth.decorators import user_passes_test +from django.core.paginator import Paginator +from django.core.urlresolvers import reverse +from django.http import Http404, HttpResponseRedirect, HttpResponse +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.utils.encoding import smart_unicode, iri_to_uri +from django.utils.translation import ugettext_lazy as _ +from django.utils import simplejson +from django.views.decorators.cache import never_cache +from rosetta.conf import settings as rosetta_settings +from rosetta.polib import pofile +from rosetta.poutil import find_pos, pagination_range +from rosetta.signals import entry_changed, post_save +from rosetta.storage import get_storage +import re +import rosetta +import datetime +import unicodedata +import hashlib +import os +from microsofttranslator import Translator, TranslateApiException + +def translate_text(language_from, language_to, text): + if language_from == language_to: + data = { 'success' : True, 'translation' : text } + else: + # run the translation: + AZURE_CLIENT_ID = getattr(settings, 'AZURE_CLIENT_ID', None) + AZURE_CLIENT_SECRET = getattr(settings, 'AZURE_CLIENT_SECRET', None) + + translator = Translator(AZURE_CLIENT_ID, AZURE_CLIENT_SECRET) + + try: + translated_text = translator.translate(text, language_to) + data = { 'success' : True, 'translation' : translated_text } + except TranslateApiException as e: + data = { 'success' : False, 'error' : "Translation API Exception: {0}".format(e.message) } + + return HttpResponse(simplejson.dumps(data), mimetype='application/json') + \ No newline at end of file diff --git a/rosetta/views.py b/rosetta/views.py index 30b668b..ff8c1e8 100644 --- a/rosetta/views.py +++ b/rosetta/views.py @@ -395,9 +395,6 @@ def translate_text(request): AZURE_CLIENT_ID = getattr(settings, 'AZURE_CLIENT_ID', None) AZURE_CLIENT_SECRET = getattr(settings, 'AZURE_CLIENT_SECRET', None) - print AZURE_CLIENT_ID - print AZURE_CLIENT_SECRET - translator = Translator(AZURE_CLIENT_ID, AZURE_CLIENT_SECRET) try: @@ -406,5 +403,4 @@ def translate_text(request): except TranslateApiException as e: data = { 'success' : False, 'error' : "Translation API Exception: {0}".format(e.message) } - return HttpResponse(simplejson.dumps(data), mimetype='application/json') - \ No newline at end of file + return HttpResponse(simplejson.dumps(data), mimetype='application/json') \ No newline at end of file From 5a51e39738a043a82896ca972b777e721d68d500 Mon Sep 17 00:00:00 2001 From: Marco Bonetti Date: Wed, 9 Jan 2013 14:16:54 +0100 Subject: [PATCH 05/75] FIXES #46 - New quoted URL system in django 1.5 --- README.rst | 2 +- rosetta/templates/rosetta/base.html | 12 ++++---- rosetta/templates/rosetta/languages.html | 9 +++--- rosetta/templates/rosetta/pofile.html | 37 ++++++++++++------------ runtests_multi_venv.sh | 23 +++++++++++++++ testproject/settings.py | 2 +- 6 files changed, 55 insertions(+), 30 deletions(-) create mode 100644 runtests_multi_venv.sh diff --git a/README.rst b/README.rst index be394c1..304ccf9 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ Features Requirements ************ -Rosetta requires Django 1.3 or later (it should work with Django 1.1 and 1.2, but it is not supported.) +Rosetta requires Django 1.3 or later ************ Installation diff --git a/rosetta/templates/rosetta/base.html b/rosetta/templates/rosetta/base.html index 19fe7a0..e3949df 100644 --- a/rosetta/templates/rosetta/base.html +++ b/rosetta/templates/rosetta/base.html @@ -1,16 +1,16 @@ - +{% load url from future %} {% block pagetitle %}Rosetta{% endblock %} - - + + - + - + diff --git a/rosetta/templates/rosetta/js/rosetta.js b/rosetta/templates/rosetta/js/rosetta.js index 4399177..a8ed143 100644 --- a/rosetta/templates/rosetta/js/rosetta.js +++ b/rosetta/templates/rosetta/js/rosetta.js @@ -7,16 +7,16 @@ google.setOnLoadCallback(function() { $('.hide', $(this).parent()).hide(); }); -{% if ENABLE_TRANSLATION_SUGGESTIONS and BING_APP_ID %} +{% if rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS and rosetta_settings.BING_APP_ID %} $('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 = '{{ MESSAGES_SOURCE_LANGUAGE_CODE }}'; + var sourceLang = '{{ rosetta_settings.MESSAGES_SOURCE_LANGUAGE_CODE }}'; var destLang = '{{ rosetta_i18n_lang_code }}'; - var app_id = '{{ BING_APP_ID }}'; + var app_id = '{{ rosetta_settings.BING_APP_ID }}'; var apiUrl = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate"; orig = unescape(orig).replace(//g,'\n').replace(//,'').replace(/<\/code>/g,'').replace(/>/g,'>').replace(/</g,'<'); @@ -50,7 +50,7 @@ google.setOnLoadCallback(function() { $($('.part',td).get(j)).css('top',textareaY + 'px'); }); }); - + $('.translation textarea').blur(function() { if($(this).val()) { $('.alert', $(this).parents('tr')).remove(); @@ -70,7 +70,7 @@ google.setOnLoadCallback(function() { } else { if (!(origs === null && trads === null)) { $(this).before(error); - return false; + return false; } } return true; @@ -78,5 +78,5 @@ google.setOnLoadCallback(function() { }); $('.translation textarea').eq(0).focus(); - + }); diff --git a/rosetta/templates/rosetta/pofile.html b/rosetta/templates/rosetta/pofile.html index df17d72..8482c0d 100644 --- a/rosetta/templates/rosetta/pofile.html +++ b/rosetta/templates/rosetta/pofile.html @@ -15,7 +15,7 @@ {% endblock %} -{% block pagetitle %}{{block.super}} - {{MESSAGES_SOURCE_LANGUAGE_NAME}} - {{rosetta_i18n_lang_name}} ({{ rosetta_i18n_pofile.percent_translated|floatformat:0 }}%){% endblock %} +{% block pagetitle %}{{block.super}} - {{rosetta_settings.MESSAGES_SOURCE_LANGUAGE_NAME}} - {{rosetta_i18n_lang_name}} ({{ rosetta_i18n_pofile.percent_translated|floatformat:0 }}%){% endblock %} {% block breadcumbs %}
@@ -33,15 +33,15 @@