/g,'').replace(/<\/code>/g,'').replace(/>/g,'>').replace(/</g,'<');
+ a.attr('class','suggesting').html('...');
+ google.language.translate(orig, "en", '{{rosetta_i18n_lang_code}}', function(result) {
+ if (!result.error) {
+ trans.val(unescape(result.translation).replace(/'/g,'\'').replace(/"/g,'"').replace(/%\s+(\([^\)]+\))\s*s/g,' %$1s '));
+ a.hide();
+ } else {
+ a.hide().before($(''+result.error.message+''));
+ }
+ });
+ return false;
+ });
+{% endif %}
+ $('td.plural').each(function(i) {
+ var td = $(this), trY = parseInt(td.closest('tr').offset().top);
+ $('textarea', $(this).closest('tr')).each(function(j) {
+ var textareaY= parseInt($(this).offset().top) - trY;
+ $($('.part',td).get(j)).css('top',textareaY + 'px');
+ });
+ });
+
+ $('.translation textarea').blur(function() {
+ if($(this).val()) {
+ $('.alert', $(this).parents('tr')).remove();
+ var RX = /%(?:\([^\s\)]*\))?[sdf]/g,
+ origs=$('.original', $(this).parents('tr')).html().match(RX),
+ trads=$(this).val().match(RX),
+ error = $('Unmatched variables');
+ if (origs && trads) {
+ for (var i = trads.length; i--;){
+ var key = trads[i];
+ if (-1 == $.inArray(key, origs)) {
+ $(this).before(error)
+ return false;
+ }
+ }
+ return true;
+ } else {
+ if (!(origs === null && trads === null)) {
+ $(this).before(error);
+ return false;
+ }
+ }
+ return true;
+ }
+ });
+
+});
diff --git a/pyweb/rosetta/templates/rosetta/languages.html b/pyweb/rosetta/templates/rosetta/languages.html
new file mode 100644
index 0000000..257f8b3
--- /dev/null
+++ b/pyweb/rosetta/templates/rosetta/languages.html
@@ -0,0 +1,45 @@
+{% extends "rosetta/base.html" %}
+{% load i18n %}
+{% block breadcumbs %}{% trans "Home" %} › {% trans "Language selection" %}{% endblock %}
+{% block pagetitle %}{% trans "Language selection" %}{% endblock %}
+{% block main %}
+ {% if has_pos %}
+ {% for lid,language,pos in languages %}
+ {% if pos %}
+
+ {{language}}
+
+
+
+ {% trans "Application" %}
+ {% trans "Progress"%}
+ {% trans "Messages" %}
+ {% trans "Translated" %}
+ {% trans "Fuzzy"%}
+ {% trans "Obsolete"%}
+ {% trans "File" %}
+
+
+
+ {% for app,path,po in pos %}
+
+ {{ app|title }}
+ {{po.percent_translated|floatformat:2}}%
+ {{po|length}}
+ {{po.translated_entries|length}}
+ {{po.fuzzy_entries|length}}
+ {{po.obsolete_entries|length}}
+ {{ path }}
+
+ {% endfor %}
+
+
+
+ {% endif %}
+ {% endfor %}
+ {% else %}
+ {% trans "Nothing to translate!" %}
+ {% trans "You haven't specified any languages in your settings file, or haven't yet generated a batch of translation catalogs." %}
+ {% blocktrans with "http://docs.djangoproject.com/en/dev/topics/i18n/#topics-i18n" as i18n_doc_link %}Please refer to Django's I18N documentation for a guide on how to set up internationalization for your project.{% endblocktrans %}
+ {% endif %}
+{% endblock %}
diff --git a/pyweb/rosetta/templates/rosetta/pofile.html b/pyweb/rosetta/templates/rosetta/pofile.html
new file mode 100644
index 0000000..8433d62
--- /dev/null
+++ b/pyweb/rosetta/templates/rosetta/pofile.html
@@ -0,0 +1,125 @@
+{% extends "rosetta/base.html" %}
+{% load rosetta i18n %}
+
+{% block header %}
+ {{block.super}}
+
+{% endblock %}
+{% block breadcumbs %}
+ {% trans "Home" %} ›
+ {{ rosetta_i18n_lang_name }} ›
+ {{ rosetta_i18n_app|title }} ›
+ {% blocktrans with rosetta_i18n_pofile.percent_translated|floatformat:2 as percent_translated %}Progress: {{ percent_translated }}%{% endblocktrans %}
+ {% if not rosetta_i18n_write %}{% trans "File is read-only: download the file when done editing!" %}{% endif %}
+{% endblock %}
+{% block pagetitle %}{% trans "English" %} - {{rosetta_i18n_lang_name}} ({{ rosetta_i18n_pofile.percent_translated|floatformat:0 }}%){% endblock %}
+{% block main %}
+ {% blocktrans %}Translate into {{rosetta_i18n_lang_name}}{% endblocktrans %}
+
+
+ - {% trans "Display:" %}
+ - {% trans "Untranslated only" %}
+ - {% trans "Translated only" %}
+ - {% trans "Fuzzy only" %}
+ - {% trans "All" %}
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/pyweb/rosetta/templatetags/__init__.py b/pyweb/rosetta/templatetags/__init__.py
new file mode 100644
index 0000000..76f7515
--- /dev/null
+++ b/pyweb/rosetta/templatetags/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright (C) 2010, mbonetti
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+"""
+
diff --git a/pyweb/rosetta/templatetags/rosetta.py b/pyweb/rosetta/templatetags/rosetta.py
new file mode 100644
index 0000000..3d3a6e5
--- /dev/null
+++ b/pyweb/rosetta/templatetags/rosetta.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright (C) 2010, mbonetti
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+"""
+
+from django import template
+from django.utils.safestring import mark_safe
+from django.utils.html import escape
+import re
+from django.template import Node
+
+register = template.Library()
+rx = re.compile(r'(%(\([^\s\)]*\))?[sd])')
+
+def format_message(message):
+ return mark_safe(rx.sub('\\1
', escape(message).replace(r'\n','
\n')))
+format_message=register.filter(format_message)
+
+
+def lines_count(message):
+ return 1 + sum([len(line)/50 for line in message.split('\n')])
+lines_count=register.filter(lines_count)
+
+def mult(a,b):
+ return int(a)*int(b)
+mult=register.filter(mult)
+
+def minus(a,b):
+ try:
+ return int(a) - int(b)
+ except:
+ return 0
+minus=register.filter(minus)
+
+
+def gt(a,b):
+ try:
+ return int(a) > int(b)
+ except:
+ return False
+gt=register.filter(gt)
+
+
+def is_fuzzy(message):
+ return message and hasattr(message, 'flags') and 'fuzzy' in message.flags
+is_fuzzy = register.filter(is_fuzzy)
+
+class RosettaCsrfTokenPlaceholder(Node):
+ def render(self, context):
+ return mark_safe(u"")
+
+def rosetta_csrf_token(parser, token):
+ try:
+ from django.template.defaulttags import csrf_token
+ return csrf_token(parser,token)
+ except ImportError:
+ return RosettaCsrfTokenPlaceholder()
+rosetta_csrf_token=register.tag(rosetta_csrf_token)
diff --git a/pyweb/rosetta/urls.py b/pyweb/rosetta/urls.py
new file mode 100644
index 0000000..4c6fa9e
--- /dev/null
+++ b/pyweb/rosetta/urls.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright (C) 2010, mbonetti
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+"""
+
+from django.conf.urls.defaults import *
+from django.views.generic.simple import direct_to_template
+urlpatterns = patterns('rosetta.views',
+ url(r'^$', 'home', name='rosetta-home'),
+ 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'),
+)
diff --git a/pyweb/rosetta/views.py b/pyweb/rosetta/views.py
new file mode 100644
index 0000000..dad7329
--- /dev/null
+++ b/pyweb/rosetta/views.py
@@ -0,0 +1,322 @@
+# -*- coding: utf-8 -*-
+
+"""
+ * Copyright (C) 2010, mbonetti
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+"""
+
+from django.conf import settings
+from django.contrib.auth.decorators import user_passes_test
+from django.core.paginator import Paginator, InvalidPage
+from django.core.urlresolvers import reverse, resolve, Resolver404
+from django.http import Http404, HttpResponseRedirect, HttpResponse
+from django.shortcuts import render_to_response
+from django.utils.encoding import smart_unicode, force_unicode, iri_to_uri
+from django.utils.translation import ugettext_lazy as _
+from django.views.decorators.cache import never_cache
+from rosetta.polib import pofile
+from rosetta.poutil import find_pos, pagination_range
+from rosetta.conf import settings as rosetta_settings
+import re, os, rosetta, datetime, unicodedata
+from django.template import RequestContext
+
+
+try:
+ resolve(settings.LOGIN_URL)
+except Resolver404:
+ try:
+ resolve('/admin/')
+ except Resolver404:
+ raise Exception('Rosetta cannot log you in!\nYou must define a LOGIN_URL in your settings if you don\'t run the Django admin site at a standard URL.')
+ else:
+ LOGIN_URL = '/admin/'
+else:
+ LOGIN_URL = settings.LOGIN_URL
+
+
+def home(request):
+ """
+ Displays a list of messages to be translated
+ """
+
+ def fix_nls(in_,out_):
+ """Fixes submitted translations by filtering carriage returns and pairing
+ newlines at the begging and end of the translated string with the original
+ """
+ if 0 == len(in_) or 0 == len(out_):
+ return out_
+
+ if "\r" in out_ and "\r" not in in_:
+ out_=out_.replace("\r",'')
+
+ if "\n" == in_[0] and "\n" != out_[0]:
+ out_ = "\n" + out_
+ elif "\n" != in_[0] and "\n" == out_[0]:
+ out_ = out_.lstrip()
+ if "\n" == in_[-1] and "\n" != out_[-1]:
+ out_ = out_ + "\n"
+ elif "\n" != in_[-1] and "\n" == out_[-1]:
+ out_ = out_.rstrip()
+ return out_
+
+ version = rosetta.get_version(True)
+ if 'rosetta_i18n_fn' in request.session:
+ rosetta_i18n_fn=request.session.get('rosetta_i18n_fn')
+ rosetta_i18n_app = get_app_name(rosetta_i18n_fn)
+ rosetta_i18n_pofile = request.session.get('rosetta_i18n_pofile')
+ rosetta_i18n_lang_code = request.session['rosetta_i18n_lang_code']
+ rosetta_i18n_lang_bidi = (rosetta_i18n_lang_code in settings.LANGUAGES_BIDI)
+ rosetta_i18n_write = request.session.get('rosetta_i18n_write', True)
+
+ if 'filter' in request.GET:
+ if request.GET.get('filter') in ('untranslated', 'translated', 'fuzzy', 'all'):
+ filter_ = request.GET.get('filter')
+ request.session['rosetta_i18n_filter'] = filter_
+ return HttpResponseRedirect(reverse('rosetta-home'))
+
+ rosetta_i18n_filter = request.session.get('rosetta_i18n_filter', 'all')
+
+ if '_next' in request.POST:
+ rx=re.compile(r'^m_([0-9]+)')
+ rx_plural=re.compile(r'^m_([0-9]+)_([0-9]+)')
+ file_change = False
+ for k in request.POST.keys():
+ if rx_plural.match(k):
+ id=int(rx_plural.match(k).groups()[0])
+ idx=int(rx_plural.match(k).groups()[1])
+ rosetta_i18n_pofile[id].msgstr_plural[str(idx)] = fix_nls(rosetta_i18n_pofile[id].msgid_plural[idx], request.POST.get(k))
+ file_change = True
+ elif rx.match(k):
+ id=int(rx.match(k).groups()[0])
+ rosetta_i18n_pofile[id].msgstr = fix_nls(rosetta_i18n_pofile[id].msgid, request.POST.get(k))
+ file_change = True
+
+ if file_change and 'fuzzy' in rosetta_i18n_pofile[id].flags and not request.POST.get('f_%d' %id, False):
+ rosetta_i18n_pofile[id].flags.remove('fuzzy')
+ elif file_change and 'fuzzy' not in rosetta_i18n_pofile[id].flags and request.POST.get('f_%d' %id, False):
+ rosetta_i18n_pofile[id].flags.append('fuzzy')
+
+ if file_change and rosetta_i18n_write:
+
+ try:
+ 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')
+ except UnicodeDecodeError:
+ pass
+ try:
+ rosetta_i18n_pofile.save()
+ rosetta_i18n_pofile.save_as_mofile(rosetta_i18n_fn.replace('.po','.mo'))
+
+ # Try auto-reloading via the WSGI daemon mode reload mechanism
+ if rosetta_settings.WSGI_AUTO_RELOAD and \
+ request.environ.has_key('mod_wsgi.process_group') and \
+ request.environ.get('mod_wsgi.process_group',None) and \
+ request.environ.has_key('SCRIPT_FILENAME') and \
+ int(request.environ.get('mod_wsgi.script_reloading', '0')):
+ try:
+ os.utime(request.environ.get('SCRIPT_FILENAME'), None)
+ except OSError:
+ pass
+
+ except:
+ request.session['rosetta_i18n_write'] = False
+
+ request.session['rosetta_i18n_pofile']=rosetta_i18n_pofile
+
+ # Retain query arguments
+ query_arg = ''
+ if 'query' in request.REQUEST:
+ 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'))
+
+
+ return HttpResponseRedirect(reverse('rosetta-home') + iri_to_uri(query_arg))
+
+
+ rosetta_i18n_lang_name = _(request.session.get('rosetta_i18n_lang_name'))
+ rosetta_i18n_lang_code = request.session.get('rosetta_i18n_lang_code')
+
+ if 'query' in request.REQUEST and request.REQUEST.get('query','').strip():
+ query=request.REQUEST.get('query').strip()
+ rx=re.compile(query, re.IGNORECASE)
+ paginator = Paginator([e for e in rosetta_i18n_pofile if rx.search(smart_unicode(e.msgstr)+smart_unicode(e.msgid)+u''.join([o[0] for o in e.occurrences]))], rosetta_settings.MESSAGES_PER_PAGE)
+ else:
+ if rosetta_i18n_filter == 'untranslated':
+ paginator = Paginator(rosetta_i18n_pofile.untranslated_entries(), rosetta_settings.MESSAGES_PER_PAGE)
+ elif rosetta_i18n_filter == 'translated':
+ paginator = Paginator(rosetta_i18n_pofile.translated_entries(), rosetta_settings.MESSAGES_PER_PAGE)
+ elif rosetta_i18n_filter == 'fuzzy':
+ paginator = Paginator(rosetta_i18n_pofile.fuzzy_entries(), rosetta_settings.MESSAGES_PER_PAGE)
+ else:
+ paginator = Paginator([e for e in rosetta_i18n_pofile if not e.obsolete], rosetta_settings.MESSAGES_PER_PAGE)
+
+ if 'page' in request.GET and int(request.GET.get('page')) <= paginator.num_pages and int(request.GET.get('page')) > 0:
+ page = int(request.GET.get('page'))
+ else:
+ page = 1
+ messages = paginator.page(page).object_list
+ if rosetta_settings.MAIN_LANGUAGE and rosetta_settings.MAIN_LANGUAGE != rosetta_i18n_lang_code:
+
+ main_language = None
+ for language in settings.LANGUAGES:
+ if language[0] == rosetta_settings.MAIN_LANGUAGE:
+ main_language = _(language[1])
+ break
+
+ fl = ("/%s/" % rosetta_settings.MAIN_LANGUAGE).join(rosetta_i18n_fn.split("/%s/" % rosetta_i18n_lang_code))
+ po = pofile(fl)
+
+ main_messages = []
+ for message in messages:
+ message.main_lang = po.find(message.msgid).msgstr
+
+ needs_pagination = paginator.num_pages > 1
+ if needs_pagination:
+ if paginator.num_pages >= 10:
+ page_range = pagination_range(1, paginator.num_pages, page)
+ else:
+ page_range = range(1,1+paginator.num_pages)
+ ADMIN_MEDIA_PREFIX = settings.ADMIN_MEDIA_PREFIX
+ ENABLE_TRANSLATION_SUGGESTIONS = rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS
+
+ return render_to_response('rosetta/pofile.html', locals(), context_instance=RequestContext(request))
+
+
+ else:
+ return list_languages(request)
+home=user_passes_test(lambda user:can_translate(user),LOGIN_URL)(home)
+home=never_cache(home)
+
+
+def download_file(request):
+ import zipfile, os
+ from StringIO import StringIO
+ # original filename
+ rosetta_i18n_fn=request.session.get('rosetta_i18n_fn', None)
+ # in-session modified catalog
+ rosetta_i18n_pofile = request.session.get('rosetta_i18n_pofile', None)
+ # language code
+ rosetta_i18n_lang_code = request.session.get('rosetta_i18n_lang_code', None)
+
+ if not rosetta_i18n_lang_code or not rosetta_i18n_pofile or not rosetta_i18n_fn:
+ return HttpResponseRedirect(reverse('rosetta-home'))
+ try:
+ if len(rosetta_i18n_fn.split('/')) >= 5:
+ offered_fn = '_'.join(rosetta_i18n_fn.split('/')[-5:])
+ else:
+ offered_fn = rosetta_i18n_fn.split('/')[-1]
+ po_fn = str(rosetta_i18n_fn.split('/')[-1])
+ mo_fn = str(po_fn.replace('.po','.mo')) # not so smart, huh
+ zipdata = StringIO()
+ zipf = zipfile.ZipFile(zipdata, mode="w")
+ zipf.writestr(po_fn, str(rosetta_i18n_pofile))
+ zipf.writestr(mo_fn, rosetta_i18n_pofile.to_binary())
+ zipf.close()
+ zipdata.seek(0)
+
+ response = HttpResponse(zipdata.read())
+ response['Content-Disposition'] = 'attachment; filename=%s.%s.zip' %(offered_fn,rosetta_i18n_lang_code)
+ response['Content-Type'] = 'application/x-zip'
+ return response
+ except Exception, e:
+ return HttpResponseRedirect(reverse('rosetta-home'))
+
+download_file=user_passes_test(lambda user:can_translate(user),LOGIN_URL)(download_file)
+download_file=never_cache(download_file)
+
+
+
+def list_languages(request):
+ """
+ Lists the languages for the current project, the gettext catalog files
+ that can be translated and their translation progress
+ """
+ languages = []
+ do_django = 'django' in request.GET
+ do_rosetta = 'rosetta' in request.GET
+ has_pos = False
+ for language in settings.LANGUAGES:
+ pos = find_pos(language[0],include_djangos=do_django,include_rosetta=do_rosetta)
+ has_pos = has_pos or len(pos)
+ languages.append(
+ (language[0],
+ _(language[1]),
+ [(get_app_name(l), os.path.realpath(l), pofile(l)) for l in pos],
+ )
+ )
+ ADMIN_MEDIA_PREFIX = settings.ADMIN_MEDIA_PREFIX
+ version = rosetta.get_version(True)
+ return render_to_response('rosetta/languages.html', locals(), context_instance=RequestContext(request))
+list_languages=user_passes_test(lambda user:can_translate(user),LOGIN_URL)(list_languages)
+list_languages=never_cache(list_languages)
+
+def get_app_name(path):
+ app = path.split("/locale")[0].split("/")[-1]
+ return app
+
+def lang_sel(request,langid,idx):
+ """
+ Selects a file to be translated
+ """
+ if langid not in [l[0] for l in settings.LANGUAGES]:
+ raise Http404
+ else:
+
+ do_django = 'django' in request.GET
+ do_rosetta = 'rosetta' in request.GET
+
+ file_ = find_pos(langid,include_djangos=do_django,include_rosetta=do_rosetta)[int(idx)]
+
+ request.session['rosetta_i18n_lang_code'] = langid
+ request.session['rosetta_i18n_lang_name'] = unicode([l[1] for l in settings.LANGUAGES if l[0] == langid][0])
+ request.session['rosetta_i18n_fn'] = file_
+ po = pofile(file_)
+ for i in range(len(po)):
+ po[i].id = i
+
+ request.session['rosetta_i18n_pofile'] = po
+ try:
+ os.utime(file_,None)
+ request.session['rosetta_i18n_write'] = True
+ except OSError:
+ request.session['rosetta_i18n_write'] = False
+
+ return HttpResponseRedirect(reverse('rosetta-home'))
+lang_sel=user_passes_test(lambda user:can_translate(user),LOGIN_URL)(lang_sel)
+lang_sel=never_cache(lang_sel)
+
+def can_translate(user):
+ if not user.is_authenticated():
+ return False
+ elif user.is_superuser:
+ return True
+ else:
+ try:
+ from django.contrib.auth.models import Group
+ translators = Group.objects.get(name='translators')
+ return translators in user.groups.all()
+ except Group.DoesNotExist:
+ return False
diff --git a/pyweb/settings.py b/pyweb/settings.py
index cc0e80e..a366eeb 100644
--- a/pyweb/settings.py
+++ b/pyweb/settings.py
@@ -190,4 +190,5 @@ INSTALLED_APPS = (
'django.contrib.sites',
'registration',
'mumble',
+ 'rosetta',
)