mirror of
https://github.com/Hopiu/django-rosetta.git
synced 2026-05-13 23:53:10 +00:00
Merge remote-tracking branch 'mbi/develop' into reflang
Conflicts: README.rst rosetta/views.py
This commit is contained in:
commit
1ff1e04460
12 changed files with 273 additions and 19 deletions
4
CHANGES
4
CHANGES
|
|
@ -1,6 +1,10 @@
|
|||
Version 0.7.2
|
||||
-------------
|
||||
* Fix for when settings imports unicode_literals for some reason (Issue #67)
|
||||
* Fixed mess with app_id between pages (Issue #68, thanks @tsouvarev)
|
||||
* Added Farsi translation. Thanks, @amiraliakbari
|
||||
* Improved the permission system, allowing for more advanced permission mechanisms. Thanks, @codeinthehole and @tangentlabs
|
||||
* Fixed the ordering of apps in the language selection screen. (Issue #73, thanks @tsouvarev, @kanu and everyone else involved in tracking this one down)
|
||||
|
||||
Version 0.7.1
|
||||
-------------
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ Rosetta can be configured via the following parameters, to be defined in your pr
|
|||
* ``ROSETTA_POFILE_WRAP_WIDTH``: Sets the line-length of the edited PO file. Set this to ``0`` to mimic ``makemessage``'s ``--no-wrap`` option. Defaults to ``78``.
|
||||
* ``ROSETTA_STORAGE_CLASS``: See the note below on Storages. Defaults to ``rosetta.storage.CacheRosettaStorage``
|
||||
* ``ROSETTA_ENABLE_REFLANG``: See the note below on Reference Language. Defaults to ``False``.
|
||||
* ``ROSETTA_ACCESS_CONTROL_FUNCTION``: An alternative function that determines if a given user can access the translation views. This function receives a ``user`` as its argument, and returns a boolean specifying whether the passed user is allowed to use Rosetta or not.
|
||||
|
||||
********
|
||||
Storages
|
||||
|
|
|
|||
36
rosetta/access.py
Normal file
36
rosetta/access.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
from django.conf import settings
|
||||
from django.utils import importlib
|
||||
|
||||
|
||||
def can_translate(user):
|
||||
return get_access_control_function()(user)
|
||||
|
||||
|
||||
def get_access_control_function():
|
||||
"""
|
||||
Return a predicate for determining if a user can access the Rosetta views
|
||||
"""
|
||||
fn_path = getattr(settings, 'ROSETTA_ACCESS_CONTROL_FUNCTION', None)
|
||||
if fn_path is None:
|
||||
return is_superuser_staff_or_in_translators_group
|
||||
# Dynamically load a permissions function
|
||||
perm_module, perm_func = fn_path.rsplit('.', 1)
|
||||
perm_module = importlib.import_module(perm_module)
|
||||
return getattr(perm_module, perm_func)
|
||||
|
||||
|
||||
# Default access control test
|
||||
def is_superuser_staff_or_in_translators_group(user):
|
||||
if not getattr(settings, 'ROSETTA_REQUIRES_AUTH', True):
|
||||
return True
|
||||
if not user.is_authenticated():
|
||||
return False
|
||||
elif user.is_superuser and user.is_staff:
|
||||
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
|
||||
|
|
@ -16,6 +16,9 @@ MAIN_LANGUAGE = getattr(settings, 'ROSETTA_MAIN_LANGUAGE', None)
|
|||
MESSAGES_SOURCE_LANGUAGE_CODE = getattr(settings, 'ROSETTA_MESSAGES_SOURCE_LANGUAGE_CODE', 'en')
|
||||
MESSAGES_SOURCE_LANGUAGE_NAME = getattr(settings, 'ROSETTA_MESSAGES_SOURCE_LANGUAGE_NAME', 'English')
|
||||
|
||||
ACCESS_CONTROL_FUNCTION = getattr(
|
||||
settings, 'ROSETTA_ACCESS_CONTROL_FUNCTION', None)
|
||||
|
||||
|
||||
"""
|
||||
When running WSGI daemon mode, using mod_wsgi 2.0c5 or later, this setting
|
||||
|
|
|
|||
BIN
rosetta/locale/fa/LC_MESSAGES/django.mo
Normal file
BIN
rosetta/locale/fa/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
206
rosetta/locale/fa/LC_MESSAGES/django.po
Normal file
206
rosetta/locale/fa/LC_MESSAGES/django.po
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
# 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 <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-04-01 18:36+0430\n"
|
||||
"PO-Revision-Date: 2013-04-01 19:03+0430\n"
|
||||
"Last-Translator: <admin@arsh.co>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: \n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
"X-Translated-Using: django-rosetta 0.7.1\n"
|
||||
|
||||
#: templates/rosetta/languages.html:5 templates/rosetta/languages.html.py:8
|
||||
msgid "Language selection"
|
||||
msgstr "انتخاب زبان"
|
||||
|
||||
#: templates/rosetta/languages.html:8 templates/rosetta/pofile.html:22
|
||||
msgid "Home"
|
||||
msgstr "خانه"
|
||||
|
||||
#: templates/rosetta/languages.html:9
|
||||
msgid ""
|
||||
"Couldn't load the specified language file. This usually happens when using "
|
||||
"the Encrypted Cookies Session Storage backend on Django 1.4 or "
|
||||
"higher.<br/>Setting ROSETTA_STORAGE_CLASS = "
|
||||
"'rosetta.storage.CacheRosettaStorage' in your settings file should fix this."
|
||||
msgstr ""
|
||||
"بارگزاری کاتالوگ زبان مورد نظر ممکن نبود. این مشکل معمولا به خاطر استفاده از"
|
||||
" Encrypted Cookies Session Storage backend در جنگو ۱.۴ یا بالاتر رخ "
|
||||
"میدهد.<br/>با افزودن خط زیر به فایل تنظیمات پروژه معمولا این مشکل را حل "
|
||||
"میکند:<br/>ROSETTA_STORAGE_CLASS = 'rosetta.storage.CacheRosettaStorage'"
|
||||
|
||||
#: templates/rosetta/languages.html:15
|
||||
msgid "Filter"
|
||||
msgstr "فیلتر"
|
||||
|
||||
#: templates/rosetta/languages.html:16
|
||||
msgid "Project"
|
||||
msgstr "پروژه"
|
||||
|
||||
#: templates/rosetta/languages.html:17
|
||||
msgid "Third party"
|
||||
msgstr "شخص ثالث"
|
||||
|
||||
#: templates/rosetta/languages.html:19 templates/rosetta/pofile.html:39
|
||||
msgid "All"
|
||||
msgstr "همه"
|
||||
|
||||
#: templates/rosetta/languages.html:32
|
||||
msgid "Application"
|
||||
msgstr "کارکرد"
|
||||
|
||||
#: templates/rosetta/languages.html:33
|
||||
msgid "Progress"
|
||||
msgstr "پیشرفت"
|
||||
|
||||
#: templates/rosetta/languages.html:34
|
||||
msgid "Messages"
|
||||
msgstr "پیامها"
|
||||
|
||||
#: templates/rosetta/languages.html:35
|
||||
msgid "Translated"
|
||||
msgstr "ترجمه شده"
|
||||
|
||||
#: templates/rosetta/languages.html:36 templates/rosetta/pofile.html:71
|
||||
msgid "Fuzzy"
|
||||
msgstr "نیاز به بازبینی"
|
||||
|
||||
#: templates/rosetta/languages.html:37
|
||||
msgid "Obsolete"
|
||||
msgstr "کهنه"
|
||||
|
||||
#: templates/rosetta/languages.html:38
|
||||
msgid "File"
|
||||
msgstr "پرونده"
|
||||
|
||||
#: templates/rosetta/languages.html:61
|
||||
msgid "Nothing to translate!"
|
||||
msgstr "موردی برای ترجمه وجود ندارد!"
|
||||
|
||||
#: templates/rosetta/languages.html:62
|
||||
msgid ""
|
||||
"You haven't specified any languages in your settings file, or haven't yet "
|
||||
"generated a batch of translation catalogs."
|
||||
msgstr ""
|
||||
"شما زبانی را در فایل تنظیمات تعیین نکردید، یا هنوز کاتالوگهای ترجمه را "
|
||||
"ایجاد نکردید."
|
||||
|
||||
#: templates/rosetta/languages.html:63
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Please refer to <a href=\"%(i18n_doc_link)s\">Django's I18N "
|
||||
"documentation</a> for a guide on how to set up internationalization for your"
|
||||
" project."
|
||||
msgstr ""
|
||||
"لطفا به <a href=\"%(i18n_doc_link)s\">مستندات جهانیسازی جنگو "
|
||||
"documentation</a> برای راهنما در مورد روش راهاندازی امکانات جهانیسازی برای"
|
||||
" پروژهتان مراجعه کنید."
|
||||
|
||||
#: templates/rosetta/pofile.html:9
|
||||
msgid "Pick another file"
|
||||
msgstr "انتخاب یک فایل دیگر"
|
||||
|
||||
#: templates/rosetta/pofile.html:10
|
||||
msgid "Download this catalog"
|
||||
msgstr "دانلود این کاتالوگ"
|
||||
|
||||
#: templates/rosetta/pofile.html:25
|
||||
#, python-format
|
||||
msgid "Progress: %(percent_translated)s%%"
|
||||
msgstr "درصد پیشرفت: %(percent_translated)s%%"
|
||||
|
||||
#: templates/rosetta/pofile.html:27
|
||||
msgid "File is read-only: download the file when done editing!"
|
||||
msgstr ""
|
||||
"فایل ترجمه غیر قابل نوشتن است، پس از اتمام ترجمه فایل حاصل را دانلود نمایید!"
|
||||
|
||||
#: templates/rosetta/pofile.html:28
|
||||
msgid ""
|
||||
"Some items in your last translation block couldn't be saved: this usually "
|
||||
"happens when the catalog file changes on disk after you last loaded it."
|
||||
msgstr ""
|
||||
"برخی موارد در آخرین دستهی ترجمه قابل ذخیره نبود: این مشکل معمولا زمانی رخ "
|
||||
"میدهد که فایل کاتالوگ روی دیسک پس از آخرین دفعهی بارگزاری آن، تغییر کرده "
|
||||
"باشد."
|
||||
|
||||
#: templates/rosetta/pofile.html:32
|
||||
#, python-format
|
||||
msgid "Translate into %(rosetta_i18n_lang_name)s"
|
||||
msgstr "در حال ترجمه به %(rosetta_i18n_lang_name)s"
|
||||
|
||||
#: templates/rosetta/pofile.html:35
|
||||
msgid "Display:"
|
||||
msgstr "نمایش:"
|
||||
|
||||
#: templates/rosetta/pofile.html:36
|
||||
msgid "Untranslated only"
|
||||
msgstr "فقط ترجمه نشدهها"
|
||||
|
||||
#: templates/rosetta/pofile.html:37
|
||||
msgid "Translated only"
|
||||
msgstr "فقط ترجمه شدهها"
|
||||
|
||||
#: templates/rosetta/pofile.html:38
|
||||
msgid "Fuzzy only"
|
||||
msgstr "فقط موارد برای بازبینی"
|
||||
|
||||
#: templates/rosetta/pofile.html:46
|
||||
msgid "Search"
|
||||
msgstr "جستجو"
|
||||
|
||||
#: templates/rosetta/pofile.html:48
|
||||
msgid "Go"
|
||||
msgstr "برو"
|
||||
|
||||
#: templates/rosetta/pofile.html:68
|
||||
msgid "Original"
|
||||
msgstr "اصلی"
|
||||
|
||||
#: templates/rosetta/pofile.html:72
|
||||
msgid "Occurrences(s)"
|
||||
msgstr "موارد رخداد"
|
||||
|
||||
#: templates/rosetta/pofile.html:86 templates/rosetta/pofile.html.py:89
|
||||
#: templates/rosetta/pofile.html:104 templates/rosetta/pofile.html.py:107
|
||||
msgid "Context hint"
|
||||
msgstr "راهنمای محلی"
|
||||
|
||||
#: templates/rosetta/pofile.html:114
|
||||
msgid "suggest"
|
||||
msgstr "پیشنهاد"
|
||||
|
||||
#: templates/rosetta/pofile.html:125
|
||||
#, python-format
|
||||
msgid "%(more_count)s more"
|
||||
msgid_plural "%(more_count)s more"
|
||||
msgstr[0] "%(more_count)s مورد بیشتر"
|
||||
msgstr[1] "%(more_count)s مورد بیشتر"
|
||||
|
||||
#: templates/rosetta/pofile.html:137
|
||||
msgid "Save and translate next block"
|
||||
msgstr "ذخیره و ترجمهی دستهی بعدی"
|
||||
|
||||
#: templates/rosetta/pofile.html:141
|
||||
msgid "Skip to page:"
|
||||
msgstr "پرش به صفحه:"
|
||||
|
||||
#: templates/rosetta/pofile.html:154
|
||||
msgid "Displaying:"
|
||||
msgstr "در حال نمایش:"
|
||||
|
||||
#: templates/rosetta/pofile.html:158
|
||||
#, python-format
|
||||
msgid "%(hits)s/%(message_number)s message"
|
||||
msgid_plural "%(hits)s/%(message_number)s messages"
|
||||
msgstr[0] "%(hits)s از %(message_number)s پیام"
|
||||
msgstr[1] "%(hits)s از %(message_number)s پیام"
|
||||
|
|
@ -113,7 +113,7 @@ def find_pos(lang, project_apps=True, django_apps=False, third_party_apps=False)
|
|||
filename = os.path.join(dirname, fn)
|
||||
if os.path.isfile(filename):
|
||||
ret.add(os.path.abspath(filename))
|
||||
return list(ret)
|
||||
return list(sorted(ret))
|
||||
|
||||
|
||||
def pagination_range(first, last, current):
|
||||
|
|
|
|||
|
|
@ -537,3 +537,22 @@ class RosettaTestCase(TestCase):
|
|||
r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world', _next='_next'))
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
self.assertTrue('Progress: 25.00%' in str(r.content))
|
||||
|
||||
def test_24_replace_access_control(self):
|
||||
# Test default access control allows access
|
||||
url = reverse('rosetta-home')
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
# Now replace access control, and check we get redirected
|
||||
settings.ROSETTA_ACCESS_CONTROL_FUNCTION = 'rosetta.tests.no_access'
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(302, response.status_code)
|
||||
|
||||
# Restore setting to default
|
||||
settings.ROSETTA_ACCESS_CONTROL_FUNCTION = None
|
||||
|
||||
|
||||
# Stubbed access control function
|
||||
def no_access(user):
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from rosetta.polib import pofile
|
|||
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
|
||||
from rosetta.access import can_translate
|
||||
import re
|
||||
import rosetta
|
||||
import unicodedata
|
||||
|
|
@ -269,6 +270,7 @@ def home(request):
|
|||
ADMIN_MEDIA_PREFIX=ADMIN_MEDIA_PREFIX,
|
||||
ADMIN_IMAGE_DIR=ADMIN_IMAGE_DIR,
|
||||
ENABLE_REFLANG=rosetta_settings.ENABLE_REFLANG,
|
||||
LANGUAGES=LANGUAGES,
|
||||
rosetta_settings=rosetta_settings,
|
||||
rosetta_i18n_lang_name=_(storage.get('rosetta_i18n_lang_name')),
|
||||
rosetta_i18n_lang_code=rosetta_i18n_lang_code,
|
||||
|
|
@ -395,7 +397,7 @@ def lang_sel(request, langid, idx):
|
|||
third_party_apps = rosetta_i18n_catalog_filter in ('all', 'third-party')
|
||||
django_apps = rosetta_i18n_catalog_filter in ('all', 'django')
|
||||
project_apps = rosetta_i18n_catalog_filter in ('all', 'project')
|
||||
file_ = find_pos(langid, project_apps=project_apps, django_apps=django_apps, third_party_apps=third_party_apps)[int(idx)]
|
||||
file_ = sorted(find_pos(langid, project_apps=project_apps, django_apps=django_apps, third_party_apps=third_party_apps), key=get_app_name)[int(idx)]
|
||||
|
||||
storage.set('rosetta_i18n_lang_code', langid)
|
||||
storage.set('rosetta_i18n_lang_name', six.text_type([l[1] for l in settings.LANGUAGES if l[0] == langid][0]))
|
||||
|
|
@ -429,20 +431,3 @@ def ref_sel(request, langid):
|
|||
return HttpResponseRedirect(reverse('rosetta-home'))
|
||||
ref_sel = never_cache(ref_sel)
|
||||
ref_sel = user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL)(ref_sel)
|
||||
|
||||
|
||||
def can_translate(user):
|
||||
if not getattr(settings, 'ROSETTA_REQUIRES_AUTH', True):
|
||||
return True
|
||||
if not user.is_authenticated():
|
||||
return False
|
||||
elif user.is_superuser and user.is_staff:
|
||||
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
|
||||
|
||||
|
|
|
|||
0
runtests.sh
Normal file → Executable file
0
runtests.sh
Normal file → Executable file
0
runtests_coverage.sh
Normal file → Executable file
0
runtests_coverage.sh
Normal file → Executable file
0
runtests_multi_venv.sh
Normal file → Executable file
0
runtests_multi_venv.sh
Normal file → Executable file
Loading…
Reference in a new issue