From f0252f1cfc54fe66be608d72d7cec2d923530ae5 Mon Sep 17 00:00:00 2001 From: Dmitry Upolovnikov Date: Mon, 21 May 2012 12:41:02 +0300 Subject: [PATCH 01/13] Added msgctxt for entrie's md5. --- .gitignore | 4 +++- rosetta/views.py | 12 ++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index c093944..7c640e4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ dist .svnignore .svnexternals build -rosetta/locale/xx/LC_MESSAGES/*.mo \ No newline at end of file +rosetta/locale/xx/LC_MESSAGES/*.mo +/.settings +/.project diff --git a/rosetta/views.py b/rosetta/views.py index a476e34..b2be811 100644 --- a/rosetta/views.py +++ b/rosetta/views.py @@ -56,7 +56,11 @@ 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")).hexdigest() + entry.md5hash = hashlib.md5( + entry.msgid.encode("utf8") + + entry.msgstr.encode("utf8") + + (entry.msgctxt and entry.msgctxt.encode("utf8") or "") + ).hexdigest() else: rosetta_i18n_pofile = request.session.get('rosetta_i18n_pofile') @@ -344,7 +348,11 @@ def lang_sel(request, langid, idx): request.session['rosetta_i18n_fn'] = file_ po = pofile(file_) for entry in po: - entry.md5hash = hashlib.md5(entry.msgid.encode("utf8") + entry.msgstr.encode("utf8")).hexdigest() + entry.md5hash = hashlib.md5( + entry.msgid.encode("utf8") + + entry.msgstr.encode("utf8") + + (entry.msgctxt and entry.msgctxt.encode("utf8") or "") + ).hexdigest() request.session['rosetta_i18n_pofile'] = po try: From 9aa9ae252691687ce5a856f9f60b1088e63783f6 Mon Sep 17 00:00:00 2001 From: Marco Bonetti Date: Sun, 27 May 2012 17:48:33 +0200 Subject: [PATCH 02/13] something out there needs STATIC_URL, cant really remember what --- testproject/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/testproject/settings.py b/testproject/settings.py index b47307d..9d049e9 100644 --- a/testproject/settings.py +++ b/testproject/settings.py @@ -50,3 +50,4 @@ ROOT_URLCONF = 'testproject.urls' DEBUG = True TEMPLATE_DEBUG = True +STATIC_URL = '/static/' From 3fd728d4158925f4c7f50e4a68f1b0a1f2b0ffd2 Mon Sep 17 00:00:00 2001 From: Marco Bonetti Date: Thu, 7 Jun 2012 13:47:13 +0200 Subject: [PATCH 03/13] dont impose a CACHES setting in the test project --- testproject/settings.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/testproject/settings.py b/testproject/settings.py index 41a54fa..98ca7ce 100644 --- a/testproject/settings.py +++ b/testproject/settings.py @@ -52,9 +52,3 @@ TEMPLATE_DEBUG = True #SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies" #ROSETTA_STORAGE_CLASS = 'rosetta.storage.SessionRosettaStorage' ROSETTA_STORAGE_CLASS = 'rosetta.storage.CacheRosettaStorage' -CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', - 'LOCATION': '127.0.0.1:11212', - } -} From f756e0c7ee7670e418fb544280c6e6fec6ea920c Mon Sep 17 00:00:00 2001 From: Marco Bonetti Date: Thu, 7 Jun 2012 16:52:28 +0200 Subject: [PATCH 04/13] Fix for issue 39 - Add msgctxt to the entry hash to differentiate entries with context. Thanks @metalpriest --- rosetta/tests/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rosetta/tests/__init__.py b/rosetta/tests/__init__.py index a655090..6908166 100644 --- a/rosetta/tests/__init__.py +++ b/rosetta/tests/__init__.py @@ -472,3 +472,14 @@ class RosettaTestCase(TestCase): self.client2.get(reverse('rosetta-language-selection', args=('xx', 0, ), kwargs=dict())) self.assertTrue(self.client.session.get('rosetta_cache_storage_key_prefix') != self.client2.session.get('rosetta_cache_storage_key_prefix')) + + def test_21_Test_Issue_gh39(self): + shutil.copy(os.path.normpath(os.path.join(self.curdir, './django.po.issue39gh.template')), self.dest_file) + + self.client.get(reverse('rosetta-pick-file') + '?filter=third-party') + r = self.client.get(reverse('rosetta-language-selection', args=('xx', 0), kwargs=dict())) + r = self.client.get(reverse('rosetta-home')) + # We have distinct hashes, even though the msgid and msgstr are identical + self.assertTrue('m_4765f7de94996d3de5975fa797c3451f' in r.content) + self.assertTrue('m_08e4e11e2243d764fc45a5a4fba5d0f2' in r.content) + From 5cbf5f80ed65a85915a86b6bda05bff34159ef03 Mon Sep 17 00:00:00 2001 From: Marco Bonetti Date: Thu, 7 Jun 2012 16:52:38 +0200 Subject: [PATCH 05/13] Fix for issue 39 - Add msgctxt to the entry hash to differentiate entries with context. Thanks @metalpriest --- rosetta/tests/django.po.issue39gh.template | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 rosetta/tests/django.po.issue39gh.template diff --git a/rosetta/tests/django.po.issue39gh.template b/rosetta/tests/django.po.issue39gh.template new file mode 100644 index 0000000..e34698c --- /dev/null +++ b/rosetta/tests/django.po.issue39gh.template @@ -0,0 +1,26 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Rosetta\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-10-21 12:21+0200\n" +"PO-Revision-Date: 2008-09-22 11:02\n" +"Last-Translator: Admin Admin \n" +"Language-Team: French \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Translated-Using: django-rosetta 0.4.RC2\n" + +#: templates/base.html:43 +msgctxt "Report (by_customer). Parent table: invoices" +msgid "String 1" +msgstr "" + +#: templates/base.html:44 +msgid "String 1" +msgstr "" From 11439587a3595aadffc394fc9384513bd081d748 Mon Sep 17 00:00:00 2001 From: Marco Bonetti Date: Thu, 7 Jun 2012 16:54:16 +0200 Subject: [PATCH 06/13] logged change for issue 39 --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index b15d6f9..f136827 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +* Add msgctxt to the entry hash to differentiate entries with context. Thanks @metalpriest (Issue #39) + Version 0.6.8 ------------- * Switched to a pluggable storage backend model to increase compatibility with Django 1.4. Cache and Session-based storages are provided. From 49b4c3e9642a36bff52b16fa4e2cf1b37971319b Mon Sep 17 00:00:00 2001 From: Marco Bonetti Date: Fri, 8 Jun 2012 15:50:12 +0200 Subject: [PATCH 07/13] Actually move to the next block when submitting a lot of translations (Issue #13) - Thanks @fetzig for the patch --- CHANGES | 1 + rosetta/tests/__init__.py | 11 ++++++++--- rosetta/views.py | 20 ++++++++++++-------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index f136827..79e080c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,4 @@ +* Actually move to the next block when submitting a lot of translations (Issue #13) * Add msgctxt to the entry hash to differentiate entries with context. Thanks @metalpriest (Issue #39) Version 0.6.8 diff --git a/rosetta/tests/__init__.py b/rosetta/tests/__init__.py index 6908166..21e0e1a 100644 --- a/rosetta/tests/__init__.py +++ b/rosetta/tests/__init__.py @@ -203,7 +203,7 @@ class RosettaTestCase(TestCase): self.assertTrue('String 1' in r.content) self.assertTrue('String 1' in r2.content) self.assertTrue('m_08e4e11e2243d764fc45a5a4fba5d0f2' in r.content) - r = self.client.post(reverse('rosetta-home'), dict(m_08e4e11e2243d764fc45a5a4fba5d0f2='Hello, world', _next='_next')) + r = self.client.post(reverse('rosetta-home'), dict(m_08e4e11e2243d764fc45a5a4fba5d0f2='Hello, world', _next='_next'), follow=True) r2 = self.client2.get(reverse('rosetta-home')) # Client 2 reloads the home, forces a reload of the catalog, @@ -220,14 +220,19 @@ class RosettaTestCase(TestCase): self.assertTrue('String 2' in r.content and 'm_e48f149a8b2e8baa81b816c0edf93890' in r.content) # client 2 posts! - r2 = self.client2.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world, from client two!', _next='_next')) + r2 = self.client2.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world, from client two!', _next='_next'), follow=True) + self.assertTrue('save-conflict' not in r2.content) # uh-oh here comes client 1 - r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world, from client one!', _next='_next')) + r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world, from client one!', _next='_next'), follow=True) # An error message is displayed self.assertTrue('save-conflict' in r.content) + # client 2 won + pofile_content = open(self.dest_file, 'r').read() + self.assertTrue('Hello, world, from client two!' in pofile_content) + # Both clients show all strings, error messages are gone r = self.client.get(reverse('rosetta-home') + '?filter=translated') self.assertTrue('save-conflict' not in r.content) diff --git a/rosetta/views.py b/rosetta/views.py index 3bd2169..b23238e 100644 --- a/rosetta/views.py +++ b/rosetta/views.py @@ -172,15 +172,11 @@ def home(request): storage.set('rosetta_i18n_pofile', rosetta_i18n_pofile) # Retain query arguments - query_arg = '' - if 'query' in request.REQUEST: - query_arg = '?query=%s' % request.REQUEST.get('query') + query_arg = '?_next=1' + if 'query' in request.GET or 'query' in request.POST: + query_arg += '&query=%s' % request.REQUEST.get('query') if 'page' in request.GET: - if query_arg: - query_arg = query_arg + '&' - else: - query_arg = '?' - query_arg = query_arg + 'page=%d' % int(request.GET.get('page')) + query_arg += '&page=%d&_next=1' % 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') @@ -203,6 +199,14 @@ 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: From d00dca3d227db8db7c96c9ae419b3334c0c86745 Mon Sep 17 00:00:00 2001 From: Jason Moiron Date: Thu, 26 Jul 2012 14:53:52 -0400 Subject: [PATCH 08/13] 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 5a51e39738a043a82896ca972b777e721d68d500 Mon Sep 17 00:00:00 2001 From: Marco Bonetti Date: Wed, 9 Jan 2013 14:16:54 +0100 Subject: [PATCH 09/13] 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 %} - - + + - + - +