Merge branch 'develop' of github.com:mbi/django-rosetta into pr/189

This commit is contained in:
Marco Bonetti 2019-01-18 11:52:58 +01:00
commit 41b8a2e790
37 changed files with 1870 additions and 1190 deletions

3
.pep8 Normal file
View file

@ -0,0 +1,3 @@
[flake8]
ignore = E501
exclude = south_migrations,migrations,.venv_*,docs

View file

@ -3,22 +3,16 @@ services: memcached
matrix:
include:
- python: "3.6"
env: TOX_ENV=py36-django18
- python: "3.6"
env: TOX_ENV=py36-django19
- python: "3.6"
env: TOX_ENV=py36-django110
- python: "3.6"
env: TOX_ENV=py36-django111
- python: "2.7"
env: TOX_ENV=py27-django18
- python: "2.7"
env: TOX_ENV=py27-django19
- python: "2.7"
env: TOX_ENV=py27-django110
- python: "2.7"
env: TOX_ENV=py27-django111
- python: "3.6"
env: TOX_ENV=py36-django20
- python: "3.6"
env: TOX_ENV=py36-django21
- python: "3.6"
env: TOX_ENV=py36-django22
install:
- pip install tox

54
CHANGES
View file

@ -1,5 +1,59 @@
Version History
===============
Version 0.9.1 (unreleased)
--------------------------
* Removed old compatibility code for Django < 1.11 (#205, thanks @claudep)
* Allow overriding rosetta login url (#210, thanks @izimobil)
* Test against Django 2.2
* Optional line number in the occurrences column (#215, thanks @pauloxnet)
* Add search in msgctxt (#213, thanks @yakky)
* Strip code tag from Yandex response. (#212, thanks @izimobil)
* Test friendly settings and better tests (#211, thanks @izimobil)
* Fix the icon-alert.svg file name (#207, thanks @matthiask)
Version 0.9.0
-------------
* Fix `ROSETTA_REQUIRES_AUTH = False` wasn't respected (#203, @BarnabasSzabolcs)
* Django-rosetta now requires Django 1.11 or newer. Rosetta 0.8.3 is the last version to support Django 1.8 through 1.10. (#204, thanks @claudep)
Version 0.8.3
-------------
* Replace the (no longer working) Microsoft translation API with the new Azure Translator API (Fixes #200 and #201, thank you @svdvonde)
Version 0.8.2
-------------
* Avoid UnicodeEncodeError when quering strings (#197, thanks @YAtOff)
* Test against Django 2.1
Version 0.8.1
-------------
* PR #194, thanks again @jbaldivieso!
* Allow searching for plural strings, both in the original and translation. (Fixes #186)
* HTML-encoding ampersands in the template (minor regression introduced with 0.8.0)
* Stop showing "None" in the search input if there was no search query submitted
Version 0.8.0
--------------
* PR #194, huge thanks to @jbaldivieso:
* Better, cleaner RESTful URLs
* Massive rewrite of Rosetta's view functions as CBVs
* Better management of cached content
* Check for PEP8 validity during tests
Version 0.7.14
--------------
* Updated installation docs (PR #190, thanks @AuHau)
* Test against Django 2.0
Version 0.7.13
--------------
* Search in comments, too (PR #174, thanks @torchingloom)

View file

@ -3,6 +3,7 @@ include LICENSE
exclude *.pyc
exclude *.sh
include tox.ini
include .pep8
recursive-include rosetta/locale *
recursive-include rosetta/tests *
recursive-include rosetta/utils *
@ -12,4 +13,7 @@ recursive-include testproject *
recursive-include docs *
prune rosetta/tests/__pycache__
prune rosetta/utils/__pycache__
prune rosetta/utils/microsofttranslator/__pycache__
prune testproject/__pycache__
prune testproject/htmlcov
prune rosetta/tests/test_app/__pycache__
prune docs/_build

View file

@ -2,7 +2,7 @@
Rosetta
=======
.. image:: https://travis-ci.org/mbi/django-rosetta.png?branch=develop
.. image:: https://travis-ci.org/mbi/django-rosetta.svg?branch=develop
:target: http://travis-ci.org/mbi/django-rosetta
@ -10,7 +10,7 @@ Rosetta is a `Django <http://www.djangoproject.com/>`_ application that eases th
Because it doesn't export any models, Rosetta doesn't create any tables in your project's database. Rosetta can be installed and uninstalled by simply adding and removing a single entry in your project's `INSTALLED_APPS` and a single line in your main ``urls.py`` file.
Note: as of version 0.7.13 django-rosetta requires Django 1.8 or later.
Note: as of version 0.7.13 django-rosetta requires Django 1.8 or later. As of version 0.9.0, django-rosetta requires Django 1.11 or later.
********
Features

View file

@ -19,7 +19,8 @@ import shlex
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..'))
from rosetta import get_version
# -- General configuration ------------------------------------------------
@ -47,7 +48,7 @@ master_doc = 'index'
# General information about the project.
project = u'Django Rosetta'
copyright = u'2008 2017 Marco Bonetti and contributors'
copyright = u'2008 2018 Marco Bonetti and contributors'
author = u'Marco Bonetti'
# The version info for the project you're documenting, acts as replacement for
@ -55,9 +56,9 @@ author = u'Marco Bonetti'
# built documents.
#
# The short X.Y version.
version = '0.7.13'
version = get_version()
# The full version, including alpha/beta/rc tags.
release = '0.7.13'
release = get_version()
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View file

@ -3,7 +3,7 @@ Installation
Requirements
------------
* As of version 0.7.13, Rosetta supports Django 1.8 through 1.11.
* As of version 0.9.0, Rosetta supports Django 1.11 and up.
Install Rosetta
@ -16,9 +16,10 @@ Install Rosetta
from django.conf import settings
if 'rosetta' in settings.INSTALLED_APPS:
urlpatterns += patterns('',
url(r'^rosetta/', include('rosetta.urls')),
)
urlpatterns += [
url(r'^rosetta/', include('rosetta.urls'))
]
Note: you can use whatever you wish as the URL prefix.

View file

@ -6,7 +6,7 @@ Rosetta can be configured via the following parameters, to be defined in your pr
* ``ROSETTA_MESSAGES_PER_PAGE``: Number of messages to display per page. Defaults to ``10``.
* ``ROSETTA_ENABLE_TRANSLATION_SUGGESTIONS``: Enable AJAX translation suggestions. Defaults to ``False``.
* ``YANDEX_TRANSLATE_KEY``: Translation suggestions from Yandex `Yandex.Translate API <http://api.yandex.com/translate/>`_. To use this service you must first `obtain an AppID key <http://api.yandex.com/key/form.xml?service=trnsl>`_, then specify the key here. Defaults to ``None``.
* ``AZURE_CLIENT_ID`` and ``AZURE_CLIENT_SECRET``: Translation suggestions using the Microsoft Azure API. To use this service, you must first `register for the service <https://datamarket.azure.com/dataset/5BA839F1-12CE-4CCE-BF57-A49D98D29A44>`_, then specify the 'Customer ID' and 'Primary Account Key' respectively, which you can find on your `account information page on Azure Marketplace <https://datamarket.azure.com/account?lang=en>`_.
* ``AZURE_CLIENT_SECRET``: Translation suggestions using the Microsoft Azure Translator API. To use this service, you must first `register for the service <https://docs.microsoft.com/en-us/azure/cognitive-services/Translator/translator-text-how-to-signup>`_, and set ``AZURE_CLIENT_SECRET`` to either of the keys listed for your subscription. 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 ``()``.
@ -21,6 +21,7 @@ Rosetta can be configured via the following parameters, to be defined in your pr
* ``ROSETTA_AUTO_COMPILE``: Determines whether the MO file is automatically compiled when the PO file is saved. Defaults to ``True``.
* ``ROSETTA_ENABLE_REFLANG``: Enables a selector for picking a reference language other than English. Defaults to ``False``.
* ``ROSETTA_SHOW_AT_ADMIN_PANEL``: Adds a handy link to Rosetta at the bottom of the Django admin apps index. Defaults to ``False``.
* ``ROSETTA_LOGIN_URL``: Use this if you want to override the login URL for rosetta. Defaults to ``settings.LOGIN_URL``.
Storages
--------

View file

@ -4,7 +4,7 @@ Usage
Generate a batch of files to translate
--------------------------------------
See `Django's documentation on Internationalization <https://docs.djangoproject.com/en/1.8/topics/i18n/translation/>`_ to set up your project to use i18n and create the ``gettext`` catalog files.
See `Django's documentation on Internationalization <https://docs.djangoproject.com/en/stable/topics/i18n/translation/>`_ to set up your project to use i18n and create the ``gettext`` catalog files.
Translate away!
---------------

View file

@ -1,4 +1,4 @@
VERSION = (0, 7, 13)
VERSION = (0, 9, 1)
default_app_config = "rosetta.apps.RosettaAppConfig"

View file

@ -1,12 +1,8 @@
import importlib
from django.conf import settings
from rosetta.conf import settings as rosetta_settings
from django.core.exceptions import ImproperlyConfigured
try:
import importlib
except ImportError:
from django.utils import importlib
def can_translate(user):
return get_access_control_function()(user)
@ -30,7 +26,7 @@ def is_superuser_staff_or_in_translators_group(user):
if not getattr(settings, 'ROSETTA_REQUIRES_AUTH', True):
return True
try:
if not user.is_authenticated():
if not user.is_authenticated:
return False
elif user.is_superuser and user.is_staff:
return True
@ -46,7 +42,7 @@ def can_translate_language(user, langid):
try:
if not rosetta_settings.ROSETTA_LANGUAGE_GROUPS:
return can_translate(user)
elif not user.is_authenticated():
elif not user.is_authenticated:
return False
elif user.is_superuser and user.is_staff:
return True

View file

@ -0,0 +1,98 @@
"""
For a description of each rosetta setting see: docs/settings.rst.
"""
from django.conf import settings as dj_settings
from django.core.signals import setting_changed
__all__ = ['settings']
class RosettaSettings(object):
"""
Class that holds rosetta settings.
The settings object is an instance of this class and is reloaded when
the ``setting_changed`` signal is dispatched.
"""
SETTINGS = {
'ROSETTA_MESSAGES_PER_PAGE': ('MESSAGES_PER_PAGE', 10),
'ROSETTA_ENABLE_TRANSLATION_SUGGESTIONS': (
'ENABLE_TRANSLATION_SUGGESTIONS', False
),
'YANDEX_TRANSLATE_KEY': ('YANDEX_TRANSLATE_KEY', None),
'AZURE_CLIENT_SECRET': ('AZURE_CLIENT_SECRET', None),
'ROSETTA_MAIN_LANGUAGE': ('MAIN_LANGUAGE', None),
'ROSETTA_MESSAGES_SOURCE_LANGUAGE_CODE': (
'MESSAGES_SOURCE_LANGUAGE_CODE', 'en'
),
'ROSETTA_MESSAGES_SOURCE_LANGUAGE_NAME': (
'MESSAGES_SOURCE_LANGUAGE_NAME', 'English'
),
'ROSETTA_ACCESS_CONTROL_FUNCTION': ('ACCESS_CONTROL_FUNCTION', None),
'ROSETTA_WSGI_AUTO_RELOAD': ('WSGI_AUTO_RELOAD', False),
'ROSETTA_UWSGI_AUTO_RELOAD': ('UWSGI_AUTO_RELOAD', False),
'ROSETTA_EXCLUDED_APPLICATIONS': ('EXCLUDED_APPLICATIONS', ()),
'ROSETTA_POFILE_WRAP_WIDTH': ('POFILE_WRAP_WIDTH', 78),
'ROSETTA_STORAGE_CLASS': (
'STORAGE_CLASS', 'rosetta.storage.CacheRosettaStorage'
),
'ROSETTA_ENABLE_REFLANG': ('ENABLE_REFLANG', False),
'ROSETTA_POFILENAMES': ('POFILENAMES', ('django.po', 'djangojs.po')),
'ROSETTA_CACHE_NAME': (
'ROSETTA_CACHE_NAME',
'rosetta' if 'rosetta' in dj_settings.CACHES else 'default'
),
'ROSETTA_REQUIRES_AUTH': ('ROSETTA_REQUIRES_AUTH', True),
'ROSETTA_EXCLUDED_PATHS': ('ROSETTA_EXCLUDED_PATHS', ()),
'ROSETTA_LANGUAGE_GROUPS': ('ROSETTA_LANGUAGE_GROUPS', False),
'ROSETTA_AUTO_COMPILE': ('AUTO_COMPILE', True),
'ROSETTA_SHOW_AT_ADMIN_PANEL': ('SHOW_AT_ADMIN_PANEL', False),
'ROSETTA_LOGIN_URL': ('LOGIN_URL', dj_settings.LOGIN_URL),
}
def __init__(self):
# make sure we don't assign self._settings directly here, to avoid
# recursion in __setattr__, we delegate to the parent instead
super(RosettaSettings, self).__setattr__('_settings', {})
self.load()
def load(self):
for user_setting, (rosetta_setting, default) in self.SETTINGS.items():
self._settings[rosetta_setting] = getattr(
dj_settings, user_setting, default
)
def reload(self):
self.__init__()
def __getattr__(self, attr):
if attr not in self._settings:
raise AttributeError(
"'RosettaSettings' object has not attribute '%s'" % attr
)
return self._settings[attr]
def __setattr__(self, attr, value):
if attr not in self._settings:
raise AttributeError(
"'RosettaSettings' object has not attribute '%s'" % attr
)
self._settings[attr] = value
# This is our global settings object
settings = RosettaSettings()
# Signal handler to reload settings when needed
def reload_settings(*args, **kwargs):
val = kwargs.get('setting')
if val in settings.SETTINGS:
settings.reload()
# Connect the setting_changed signal to our handler
setting_changed.connect(reload_settings)

View file

@ -1,90 +0,0 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
# Number of messages to display per page.
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://translate.yandex.com/apikeys
YANDEX_TRANSLATE_KEY = getattr(settings, 'YANDEX_TRANSLATE_KEY', None)
# Can be obtained for free here: https://ssl.bing.com/webmaster/Developers/AppIds/
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)
# Change these if the source language in your PO files isn't English
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
controls whether the contents of the gettext catalog files should be
automatically reloaded by the WSGI processes each time they are modified.
Notes:
* The WSGI daemon process must have write permissions on the WSGI script file
(as defined by the WSGIScriptAlias directive.)
* WSGIScriptReloading must be set to On (it is by default)
* For performance reasons, this setting should be disabled in production environments
* When a common rosetta installation is shared among different Django projects,
each one running in its own distinct WSGI virtual host, you can activate
auto-reloading in individual projects by enabling this setting in the project's
own configuration file, i.e. in the project's settings.py
Refs:
* http://code.google.com/p/modwsgi/wiki/ReloadingSourceCode
* http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIReloadMechanism
"""
WSGI_AUTO_RELOAD = getattr(settings, 'ROSETTA_WSGI_AUTO_RELOAD', False)
UWSGI_AUTO_RELOAD = getattr(settings, 'ROSETTA_UWSGI_AUTO_RELOAD', False)
# Exclude applications defined in this list from being translated
EXCLUDED_APPLICATIONS = getattr(settings, 'ROSETTA_EXCLUDED_APPLICATIONS', ())
# Line length of the updated PO file
POFILE_WRAP_WIDTH = getattr(settings, 'ROSETTA_POFILE_WRAP_WIDTH', 78)
# Storage class to handle temporary data storage
STORAGE_CLASS = getattr(settings, 'ROSETTA_STORAGE_CLASS', 'rosetta.storage.CacheRosettaStorage')
ENABLE_REFLANG = getattr(settings, 'ROSETTA_ENABLE_REFLANG', False)
# Allow overriding of the default filenames, you mostly won't need to change this
POFILENAMES = getattr(settings, 'ROSETTA_POFILENAMES', ('django.po', 'djangojs.po'))
ROSETTA_CACHE_NAME = getattr(settings, 'ROSETTA_CACHE_NAME', 'rosetta'
if 'rosetta' in settings.CACHES else 'default')
# Require users to be authenticated (and Superusers or in group "translators").
# Set this to False at your own risk
ROSETTA_REQUIRES_AUTH = getattr(settings, 'ROSETTA_REQUIRES_AUTH', True)
# Exclude paths defined in this list from being searched (usually ends with "locale")
ROSETTA_EXCLUDED_PATHS = getattr(settings, 'ROSETTA_EXCLUDED_PATHS', ())
# Set to True to enable language-specific groups, which can be used to give
# different translators access to different languages. Instead of creating a
# 'translators` group, create individual per-language groups, e.g.
# 'translators-de', 'translators-fr', ...
ROSETTA_LANGUAGE_GROUPS = getattr(settings, 'ROSETTA_LANGUAGE_GROUPS', False)
# Determines whether the MO file is automatically compiled when the PO file is saved.
AUTO_COMPILE = getattr(settings, 'ROSETTA_AUTO_COMPILE', True)
SHOW_AT_ADMIN_PANEL = getattr(settings, 'ROSETTA_SHOW_AT_ADMIN_PANEL', False)

View file

@ -1,2 +0,0 @@
from django.db import models
# Create your models here.

View file

@ -1,10 +1,9 @@
from datetime import datetime
from django.conf import settings
from django.conf import settings, ENVIRONMENT_VARIABLE
from rosetta.conf import settings as rosetta_settings
import django
import os
import inspect
from django.apps import AppConfig, apps
from django.apps import apps
from django.utils import timezone
from django.core.cache import caches
cache = caches[rosetta_settings.ROSETTA_CACHE_NAME]
@ -36,7 +35,13 @@ def find_pos(lang, project_apps=True, django_apps=False, third_party_apps=False)
paths = []
# project/locale
parts = settings.SETTINGS_MODULE.split('.')
if settings.SETTINGS_MODULE:
parts = settings.SETTINGS_MODULE.split('.')
else:
# if settings.SETTINGS_MODULE is None, we are probably in "test" mode
# and override_settings() was used
# see: https://code.djangoproject.com/ticket/25911
parts = os.environ.get(ENVIRONMENT_VARIABLE).split('.')
project = __import__(parts[0], {}, {}, [])
abs_project_path = os.path.normpath(os.path.abspath(os.path.dirname(project.__file__)))
if project_apps:
@ -61,27 +66,12 @@ def find_pos(lang, project_apps=True, django_apps=False, third_party_apps=False)
if os.path.isdir(localepath):
paths.append(localepath)
# project/app/locale
has_appconfig = False
for appname in settings.INSTALLED_APPS:
if rosetta_settings.EXCLUDED_APPLICATIONS and appname in rosetta_settings.EXCLUDED_APPLICATIONS:
for app_ in apps.get_app_configs():
if rosetta_settings.EXCLUDED_APPLICATIONS and app_.name in rosetta_settings.EXCLUDED_APPLICATIONS:
continue
p = appname.rfind('.')
if p >= 0:
app = getattr(__import__(appname[:p], {}, {}, [str(appname[p + 1:])]), appname[p + 1:])
else:
app = __import__(appname, {}, {}, [])
# For django 1.7, an imported INSTALLED_APP can be an AppConfig instead
# of an app module. This code converts the AppConfig to its application
# module.
if django.VERSION[0:2] >= (1, 7):
if inspect.isclass(app) and issubclass(app, AppConfig):
has_appconfig = True
continue
app_path = os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(app.__file__), 'locale')))
app_path = app_.path
# django apps
if 'contrib' in app_path and 'django' in app_path and not django_apps:
continue
@ -94,43 +84,19 @@ def find_pos(lang, project_apps=True, django_apps=False, third_party_apps=False)
if not project_apps and abs_project_path in app_path:
continue
if os.path.isdir(app_path):
paths.append(app_path)
# Handling with AppConfigs is a wee messy, but we can simply get all the
# app paths directly from apps object
if has_appconfig:
for app_ in apps.get_app_configs():
if rosetta_settings.EXCLUDED_APPLICATIONS and app_.name in rosetta_settings.EXCLUDED_APPLICATIONS:
continue
app_path = app_.path
# django apps
if 'contrib' in app_path and 'django' in app_path and not django_apps:
continue
# third party external
if not third_party_apps and abs_project_path not in app_path:
continue
# local apps
if not project_apps and abs_project_path in app_path:
continue
if os.path.exists(os.path.abspath(os.path.join(app_path, 'locale'))):
paths.append(os.path.abspath(os.path.join(app_path, 'locale')))
if os.path.exists(os.path.abspath(os.path.join(app_path, '..', 'locale'))):
paths.append(os.path.abspath(os.path.join(app_path, '..', 'locale')))
if os.path.exists(os.path.abspath(os.path.join(app_path, 'locale'))):
paths.append(os.path.abspath(os.path.join(app_path, 'locale')))
if os.path.exists(os.path.abspath(os.path.join(app_path, '..', 'locale'))):
paths.append(os.path.abspath(os.path.join(app_path, '..', 'locale')))
ret = set()
langs = [lang, ]
langs = [lang]
if u'-' in lang:
_l, _c = map(lambda x: x.lower(), lang.split(u'-', 1))
langs += [u'%s_%s' % (_l, _c), u'%s_%s' % (_l, _c.upper()), u'%s_%s' % (_l, _c.capitalize()),]
langs += [u'%s_%s' % (_l, _c), u'%s_%s' % (_l, _c.upper()), u'%s_%s' % (_l, _c.capitalize())]
elif u'_' in lang:
_l, _c = map(lambda x: x.lower(), lang.split(u'_', 1))
langs += [u'%s-%s' % (_l, _c), u'%s-%s' % (_l, _c.upper()), u'%s_%s' % (_l, _c.capitalize()),]
langs += [u'%s-%s' % (_l, _c), u'%s-%s' % (_l, _c.upper()), u'%s_%s' % (_l, _c.capitalize())]
paths = map(os.path.normpath, paths)
paths = list(set(paths))

View file

@ -1,8 +1,8 @@
from django import dispatch
entry_changed = dispatch.Signal(
providing_args=["user", "old_msgstr", "old_fuzzy", "pofile", "language_code",]
providing_args=["user", "old_msgstr", "old_fuzzy", "pofile", "language_code"]
)
post_save = dispatch.Signal(
providing_args=["language_code","request",]
providing_args=["language_code", "request"]
)

View file

@ -1,23 +1,13 @@
from django.conf import settings
from django.core.cache import caches
from django.core.exceptions import ImproperlyConfigured
from rosetta.conf import settings as rosetta_settings
import hashlib
import importlib
import time
import six
import django
try:
from django.core.cache import caches
cache = caches[rosetta_settings.ROSETTA_CACHE_NAME]
except ImportError:
from django.core.cache import get_cache
cache = get_cache(rosetta_settings.ROSETTA_CACHE_NAME)
try:
import importlib
except ImportError:
from django.utils import importlib
cache = caches[rosetta_settings.ROSETTA_CACHE_NAME]
class BaseRosettaStorage(object):
@ -55,8 +45,8 @@ class SessionRosettaStorage(BaseRosettaStorage):
def __init__(self, request):
super(SessionRosettaStorage, self).__init__(request)
if 'signed_cookies' in settings.SESSION_ENGINE and django.VERSION[1] >= 6 and 'pickle' not in settings.SESSION_SERIALIZER.lower():
raise ImproperlyConfigured("Sorry, but django-rosetta doesn't support the `signed_cookies` SESSION_ENGINE in Django >= 1.6, because rosetta specific session files cannot be serialized.")
if 'signed_cookies' in settings.SESSION_ENGINE and 'pickle' not in settings.SESSION_SERIALIZER.lower():
raise ImproperlyConfigured("Sorry, but django-rosetta doesn't support the `signed_cookies` SESSION_ENGINE, because rosetta specific session files cannot be serialized.")
def get(self, key, default=None):
if key in self.request.session:
@ -101,19 +91,19 @@ class CacheRosettaStorage(BaseRosettaStorage):
self.delete('rosetta_cache_test')
def get(self, key, default=None):
#print ('get', self._key_prefix + key)
# print ('get', self._key_prefix + key)
return cache.get(self._key_prefix + key, default)
def set(self, key, val):
#print ('set', self._key_prefix + key)
# print ('set', self._key_prefix + key)
cache.set(self._key_prefix + key, val, 86400)
def has(self, key):
#print ('has', self._key_prefix + key)
# print ('has', self._key_prefix + key)
return (self._key_prefix + key) in cache
def delete(self, key):
#print ('del', self._key_prefix + key)
# print ('del', self._key_prefix + key)
cache.delete(self._key_prefix + key)

View file

@ -7,11 +7,11 @@
<div class="app-rosetta module">
<table>
<caption>
<a class="section" href="{% url 'rosetta-home' %}">Rosetta</a>
<a class="section" href="{% url 'rosetta-file-list' po_filter='project' %}">Rosetta</a>
</caption>
<tbody><tr>
<th scope="row"><a href="{% url 'rosetta-home' %}">{% trans "Translations" %}</a></th>
<td colspan="2"><a href="{% url 'rosetta-home' %}" class="changelink">{% trans "Change" %}</a></td>
<th scope="row"><a href="{% url 'rosetta-file-list' po_filter='project' %}">{% trans "Translations" %}</a></th>
<td colspan="2"><a href="{% url 'rosetta-file-list' po_filter='project' %}" class="changelink">{% trans "Change" %}</a></td>
</tr>
</tbody></table>
</div>

View file

@ -1,4 +1,4 @@
<!DOCTYPE html>{% load admin_static %}
<!DOCTYPE html>{% load static %}
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>{% block pagetitle %}Rosetta{% endblock %}</title>
@ -24,7 +24,7 @@
<div id="header">
{% block header %}
<div id="branding">
<h1 id="site-name"><a href="{% url 'rosetta-pick-file' %}">Rosetta</a> </h1>
<h1 id="site-name"><a href="{% url 'rosetta-file-list' po_filter='project' %}">Rosetta</a> </h1>
</div>
{% endblock %}
</div>

View file

@ -1,3 +1,4 @@
{% load static %}
#user-tools p span { margin-left:2em; }
table{width:100%;}
td.translation label {font-size: 95%;}
@ -23,7 +24,7 @@ th.c,td.c {text-align: center;}
td.original code {font-size:90%; padding: 0 1px; }
tr.row2 td.original code {background-color:#FFB2A5; padding: 0 0.3em;}
tr.row1 td.original code {background-color:#FFB2A5;}
.alert { font-weight:bold;padding:4px 5px 4px 25px; margin-left:1em; color: red; background:transparent url({{ADMIN_MEDIA_PREFIX}}img/admin/icon_alert.gif) 5px .3em no-repeat; }
.alert { font-weight:bold;padding:4px 5px 4px 25px; margin-left:1em; color: red; background:transparent url({% static 'admin/img/icon-alert.svg' %}) 5px .3em no-repeat; }
p.errornote { margin-top:0.4em;}
#footer { clear:both; color:#999; font-size:9px; margin:0 6px; text-align:left;}
#changelist table tbody td:first-child, #changelist table thead th:first-child {text-align: left;}
@ -33,4 +34,4 @@ div.module {margin-bottom: 20px;}
#toolbar { height:20px}
#toolbar #translate-all { float:right}
#toolbar form { float:left; }
#changelist table thead th { padding: 2px 5px; }
#changelist table thead th { padding: 2px 5px; }

View file

@ -4,7 +4,7 @@
{% block pagetitle %}{{block.super}} - {% trans "Language selection" %}{% endblock %}
{% block breadcumbs %}
<div><a href="{% url 'rosetta-pick-file' %}">{% trans "Home" %}</a> &rsaquo; {% trans "Language selection" %}</div>
<div><a href="{% url 'rosetta-file-list' po_filter=po_filter %}">{% trans "Home" %}</a> &rsaquo; {% trans "Language selection" %}</div>
{% if do_session_warn %}<p class="errornote session-warn">{% trans "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." %}</p>{% endif %}
{% endblock %}
@ -12,10 +12,10 @@
<h1>&nbsp;</h1>
<ul class="object-tools">
<li class="nobubble">{% trans "Filter" %}:</li>
<li{% ifequal rosetta_i18n_catalog_filter 'project' %} class="active"{% endifequal %}><a href="?filter=project">{% trans "Project" %}</a></li>
<li{% ifequal rosetta_i18n_catalog_filter 'third-party' %} class="active"{% endifequal %}><a href="?filter=third-party">{% trans "Third party" %}</a></li>
<li{% ifequal rosetta_i18n_catalog_filter 'django' %} class="active"{% endifequal %}><a href="?filter=django">Django</a></li>
<li{% ifequal rosetta_i18n_catalog_filter 'all' %} class="active"{% endifequal %}><a href="?filter=all">{% trans "All" %}</a></li>
<li{% ifequal po_filter 'project' %} class="active"{% endifequal %}><a href="{% url 'rosetta-file-list' po_filter='project' %}">{% trans "Project" %}</a></li>
<li{% ifequal po_filter 'third-party' %} class="active"{% endifequal %}><a href="{% url 'rosetta-file-list' po_filter='third-party' %}">{% trans "Third party" %}</a></li>
<li{% ifequal po_filter 'django' %} class="active"{% endifequal %}><a href="{% url 'rosetta-file-list' po_filter='django' %}">Django</a></li>
<li{% ifequal po_filter 'all' %} class="active"{% endifequal %}><a href="{% url 'rosetta-file-list' po_filter='all' %}">{% trans "All" %}</a></li>
</ul>
{% if has_pos %}
@ -40,7 +40,7 @@
<tbody>
{% for app,path,po in pos %}
<tr class="{% cycle 'row1' 'row2' %}">
<td><a href="{% url 'rosetta-language-selection' lid forloop.counter0 %}">{{ app|title }}</a></td>
<td><a href="{% url 'rosetta-form' po_filter=po_filter lang_id=lid idx=forloop.counter0 %}">{{ app|title }}</a></td>
<td class="ch-progress r">{{po.percent_translated}}%</td>
{% with po.untranslated_entries|length as len_untranslated_entries %}
<td class="ch-messages r">{{po.translated_entries|length|add:len_untranslated_entries}}</td>

View file

@ -1,12 +1,12 @@
{% extends "rosetta/base.html" %}{% load admin_static %}
{% extends "rosetta/base.html" %}{% load static %}
{% load rosetta i18n %}
{% block header %}
{{block.super}}
<div id="user-tools">
<p>
<span><a href="{% url 'rosetta-pick-file' %}">{% trans "Pick another file" %}</a> /
<a href="{% url 'rosetta-download-file' %}">{% trans "Download this catalog" %}</a></span>
<span><a href="{% url 'rosetta-file-list' po_filter=po_filter %}">{% trans "Pick another file" %}</a> /
<a href="{% url 'rosetta-download-file' po_filter=po_filter lang_id=lang_id idx=idx %}">{% trans "Download this catalog" %}</a></span>
</p>
</div>
<script type="text/javascript">
@ -18,13 +18,12 @@
{% block breadcumbs %}
<div>
<a href="{% url 'rosetta-pick-file' %}">{% trans "Home" %}</a> &rsaquo;
<a href="{% url 'rosetta-file-list' po_filter=po_filter %}">{% trans "Home" %}</a> &rsaquo;
{{ rosetta_i18n_lang_name }} &rsaquo;
{{ rosetta_i18n_app|title }} &rsaquo;
{% blocktrans with rosetta_i18n_pofile.percent_translated as percent_translated %}Progress: {{ percent_translated }}%{% endblocktrans %}
</div>
{% if not rosetta_i18n_write %}<p class="errornote read-only">{% trans "File is read-only: download the file when done editing!" %}</p>{% endif %}
{% if rosetta_last_save_error %}<p class="errornote save-conflict">{% trans "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." %}</p>{% endif %}
{% if messages %}
<div class="messages errornote save-conflict">
{% for message in messages %}
@ -39,38 +38,37 @@
<ul class="object-tools">
<li class="nobubble">{% trans "Display:" %}</li>
<li {% if rosetta_i18n_filter == 'untranslated' %}class="active"{% endif %}><a href="?filter=untranslated">{% trans "Untranslated only" %}</a></li>
<li {% if rosetta_i18n_filter == 'translated' %}class="active"{% endif %}><a href="?filter=translated">{% trans "Translated only" %}</a></li>
<li {% if rosetta_i18n_filter == 'fuzzy' %}class="active"{% endif %}><a href="?filter=fuzzy">{% trans "Fuzzy only" %}</a></li>
<li {% if rosetta_i18n_filter == 'all' %}class="active"{% endif %}><a href="?filter=all">{% trans "All" %}</a></li>
<li {% if rosetta_i18n_filter == 'untranslated' %}class="active"{% endif %}><a href="?{{ filter_query_string_base }}&amp;msg_filter=untranslated">{% trans "Untranslated only" %}</a></li>
<li {% if rosetta_i18n_filter == 'translated' %}class="active"{% endif %}><a href="?{{ filter_query_string_base }}&amp;msg_filter=translated">{% trans "Translated only" %}</a></li>
<li {% if rosetta_i18n_filter == 'fuzzy' %}class="active"{% endif %}><a href="?{{ filter_query_string_base }}&amp;msg_filter=fuzzy">{% trans "Fuzzy only" %}</a></li>
<li {% if rosetta_i18n_filter == 'all' %}class="active"{% endif %}><a href="?{{ filter_query_string_base }}&amp;msg_filter=all">{% trans "All" %}</a></li>
</ul>
<div id="changelist" class="module{% if rosetta_i18n_lang_bidi %} rtl{% endif %}">
<div id="toolbar">
<form id="changelist-search" action="" method="post">
<form id="changelist-search" action="" method="get">
<div>
{% rosetta_csrf_token %}
<label for="searchbar"><img src="{% static "admin/img/icon_searchbox_rosetta.png" %}" alt="{% trans "Search" %}" /></label>
<input type="text" size="40" name="query" value="{% if query %}{{query}}{% endif %}" id="searchbar" tabindex="0" />
<input type="submit" name="search" value="{% trans "Go" %}" />
<input type="submit" name="s" value="{% trans "Go" %}" />
</div>
</form>
</div>
{% if ENABLE_REFLANG %}
{% if rosetta_settings.ENABLE_REFLANG %}
<div class="actions">
<label for="ref-language-selector">{% trans "Reference language" %}:</label>
<select class="select-across" id="ref-language-selector" onchange="javascript:window.location.href = this.value;">
{% for langid, langname in LANGUAGES %}
<option{% ifequal ref_lang langid %} selected="selected"{% endifequal %} value="{% url 'rosetta-reference-selection' langid=langid %}">{{langname}}</option>
<option{% ifequal ref_lang langid %} selected="selected"{% endifequal %} value="?ref_lang={{ langid }}">{{langname}}</option>
{% endfor %}
</select>
</div>
{% endif %}
<form method="post" action="" class="results">
{% rosetta_csrf_token %}
{% csrf_token %}
<table id="result_list">
<thead>
<tr>
@ -108,7 +106,7 @@
</td>
{% else %}
<td class="original">
{% if ENABLE_REFLANG %}
{% if rosetta_settings.ENABLE_REFLANG %}
<span class="message">{{ message.ref_txt|format_message|linebreaksbr }}</span>
{% else %}
<span class="message">{{ message.msgid|format_message|linebreaksbr }}</span>
@ -132,7 +130,7 @@
</td>
<td class="location">
{% for fn,lineno in message.occurrences %}
<code{% if forloop.counter|gt:"3" %} class="hide"{% endif %}>{{ fn }}:{{lineno}}</code>
<code{% if forloop.counter|gt:"3" %} class="hide"{% endif %}>{{ fn }}{% if lineno %}:{{lineno}}{% endif %}</code>
{% endfor %}
{% if message.occurrences|length|gt:"3" %}
<a href="#">&hellip; ({% blocktrans count message.occurrences|length|minus:"3" as more_count %}{{more_count}} more{% plural %}{{more_count}} more{% endblocktrans %})</a>
@ -159,7 +157,7 @@
{% if i == page %}
<span class="this-page">{{i}}</span>
{% else %}
<a href="?page={{i}}{% if query %}&amp;query={{query}}{% endif %}">{{i}}</a>
<a href="?{{ pagination_query_string_base }}&amp;page={{i}}">{{i}}</a>
{% endif %}
{% endif %}
{% endfor %}

View file

@ -8,7 +8,7 @@ google.setOnLoadCallback(function() {
});
{% if rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS %}
{% if rosetta_settings.AZURE_CLIENT_ID and rosetta_settings.AZURE_CLIENT_SECRET or rosetta_settings.GOOGLE_TRANSLATE %}
{% if rosetta_settings.AZURE_CLIENT_SECRET %}
$('a.suggest').click(function(e){
e.preventDefault();
var a = $(this);
@ -16,12 +16,12 @@ google.setOnLoadCallback(function() {
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 }}';
var destLang = '{{ rosetta_i18n_lang_code_normalized }}';
orig = unescape(orig).replace(/<br\s?\/?>/g,'\n').replace(/<code>/,'').replace(/<\/code>/g,'').replace(/&gt;/g,'>').replace(/&lt;/g,'<');
a.attr('class','suggesting').html('...');
$.getJSON("./translate/", {
$.getJSON("{% url 'rosetta.translate_text' %}", {
from: sourceLang,
to: destLang,
text: orig
@ -36,9 +36,7 @@ google.setOnLoadCallback(function() {
}
);
});
{% endif %}
{% if rosetta_settings.YANDEX_TRANSLATE_KEY %}
{% elif rosetta_settings.YANDEX_TRANSLATE_KEY %}
$('a.suggest').click(function(e){
e.preventDefault();
var a = $(this);
@ -66,7 +64,7 @@ google.setOnLoadCallback(function() {
dataType: 'jsonp',
success: function(response) {
if (response.code == 200) {
trans.val(response.text[0].replace(/<br>/g, '\n').replace(/&lt;/g, '<').replace(/&gt;/g, '>'));
trans.val(response.text[0].replace(/<br>/g, '\n').replace(/<\/?code>/g, '').replace(/&lt;/g, '<').replace(/&gt;/g, '>'));
a.hide();
} else {
a.text(response);

View file

@ -2,7 +2,6 @@ from django import template
from django.utils.safestring import mark_safe
from django.utils.html import escape
import re
from django.template import Node
import six
@ -48,7 +47,7 @@ def do_incr(parser, token):
name = args[1]
if not hasattr(parser, '_namedIncrNodes'):
parser._namedIncrNodes = {}
if not name in parser._namedIncrNodes:
if name not in parser._namedIncrNodes:
parser._namedIncrNodes[name] = IncrNode(0)
return parser._namedIncrNodes[name]
do_incr = register.tag('increment', do_incr)
@ -66,17 +65,3 @@ class IncrNode(template.Node):
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"<!-- csrf token placeholder -->")
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)

View file

@ -1 +1 @@
from .tests import *
from .tests import * # NOQA

View file

@ -0,0 +1,38 @@
# 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.
#
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 <admin@admin.com>\n"
"Language-Team: French <LL@li.org>\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"
msgid "String 1"
msgstr ""
msgid "String 2"
msgstr ""
#. Translators: This is a text of the base template
#: templates/base.html:43
msgid "String 3 with comment"
msgstr ""
msgctxt "Context hint"
msgid "String 4"
msgstr ""
msgid "%d Child"
msgid_plural "%d Childrenen"
msgstr[0] "%d Tchilt"
msgstr[1] "%d Tchildren"

View file

@ -0,0 +1,171 @@
# 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.
#
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 <admin@admin.com>\n"
"Language-Team: French <LL@li.org>\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"
msgid "String 1"
msgstr ""
msgid "String 2"
msgstr ""
msgid "String 3"
msgstr ""
msgid "String 4"
msgstr ""
msgid "String 5"
msgstr ""
msgid "String 6"
msgstr ""
msgid "String 7"
msgstr ""
msgid "String 8"
msgstr ""
msgid "String 9"
msgstr ""
msgid "String 10"
msgstr ""
msgid "String 11"
msgstr ""
msgid "String 12"
msgstr ""
msgid "String 13"
msgstr ""
msgid "String 14"
msgstr ""
msgid "String 15"
msgstr ""
msgid "String 16"
msgstr ""
msgid "String 17"
msgstr ""
msgid "String 18"
msgstr ""
msgid "String 19"
msgstr ""
msgid "String 20"
msgstr ""
msgid "String 21"
msgstr ""
msgid "String 22"
msgstr ""
msgid "String 23"
msgstr ""
msgid "String 24"
msgstr ""
msgid "String 25"
msgstr ""
msgid "String 26"
msgstr ""
msgid "String 27"
msgstr ""
msgid "String 28"
msgstr ""
msgid "String 29"
msgstr ""
msgid "String 30"
msgstr ""
msgid "String 31"
msgstr ""
msgid "String 32"
msgstr ""
msgid "String 33"
msgstr ""
msgid "String 34"
msgstr ""
msgid "String 35"
msgstr ""
msgid "String 36"
msgstr ""
msgid "String 37"
msgstr ""
msgid "String 38"
msgstr ""
msgid "String 39"
msgstr ""
msgid "String 40"
msgstr ""
msgid "String 41"
msgstr ""
msgid "String 42"
msgstr ""
msgid "String 43"
msgstr ""
msgid "String 44"
msgstr ""
msgid "String 45"
msgstr ""
msgid "String 46"
msgstr ""
msgid "String 47"
msgstr ""
msgid "String 48"
msgstr ""
msgid "String 49"
msgstr ""
#. Translators: consectetur adipisicing
#: templates/eiusmod/tempor.html:43
msgctxt "purus pellentesque"
msgid "Lorem ipsum"
msgstr "dolor sit amet"

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,45 @@
from django.conf.urls import url
from .views import (home, list_languages, download_file, lang_sel, translate_text, ref_sel)
from django.urls import reverse_lazy
from django.views.generic.base import RedirectView
from . import views
urlpatterns = [
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<langid>[\w\-_\.]+)/(?P<idx>\d+)/$', lang_sel, name='rosetta-language-selection'),
url(r'^select-ref/(?P<langid>[\w\-_\.]+)/$', ref_sel, name='rosetta-reference-selection'),
url(r'^translate/$', translate_text, name='translate_text'),
url(r'^$',
RedirectView.as_view(
url=reverse_lazy('rosetta-file-list', kwargs={'po_filter': 'project'}),
permanent=False
),
name='rosetta-old-home-redirect',
),
url(r'^files/$',
RedirectView.as_view(
url=reverse_lazy('rosetta-file-list', kwargs={'po_filter': 'project'}),
permanent=False
),
name='rosetta-file-list-redirect',
),
url(r'^files/(?P<po_filter>[\w-]+)/$',
views.TranslationFileListView.as_view(),
name='rosetta-file-list',
),
url(r'^files/(?P<po_filter>[\w-]+)/(?P<lang_id>[\w\-_\.]+)/(?P<idx>\d+)/$',
views.TranslationFormView.as_view(),
name='rosetta-form',
),
url(r'^files/(?P<po_filter>[\w-]+)/(?P<lang_id>[\w\-_\.]+)/(?P<idx>\d+)/download/$',
views.TranslationFileDownload.as_view(),
name='rosetta-download-file',
),
url(r'^translate/$',
views.translate_text,
name='rosetta.translate_text',
),
]

File diff suppressed because it is too large Load diff

View file

@ -25,10 +25,14 @@ class Tox(test_command):
errno = tox.cmdline(args=args)
sys.exit(errno)
with open('README.rst') as readme:
long_description = readme.read()
setup(
name='django-rosetta',
version=__import__('rosetta').get_version(limit=3),
description='A Django application that eases the translation of Django projects',
long_description=long_description,
author='Marco Bonetti',
author_email='mbonetti@gmail.com',
url='https://github.com/mbi/django-rosetta',
@ -45,18 +49,18 @@ setup(
'Topic :: Software Development :: Internationalization',
'Framework :: Django',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
],
include_package_data=True,
zip_safe=False,
install_requires=[
'six >=1.2.0',
'Django >= 1.7',
'Django >= 1.11',
'requests >= 2.1.0',
'polib >= 1.0.6',
'microsofttranslator >= 0.7'
'polib >= 1.1.0'
],
tests_require=['tox'],
tests_require=['tox', 'vcrpy'],
cmdclass={'test': Tox},
)

4
testproject/coverage.sh Normal file
View file

@ -0,0 +1,4 @@
#!/bin/sh
coverage run --rcfile .coveragerc manage.py test --failfast rosetta
coverage xml
coverage html

View file

@ -1,27 +0,0 @@
interactions:
- request:
body: null
headers:
Connection: [close]
Host: [!!python/unicode translate.google.com]
!!python/unicode User-Agent: [!!python/unicode Mozilla/4.0]
method: GET
uri: http://translate.google.com/translate_a/single?tl=fr&client=a&q=Hello%2C+world%21&dt=t&sl=en&oe=UTF-8&ie=UTF-8
response:
body: {string: !!python/unicode '[[["Bonjour le monde!","Hello, world!",,,1]],,"en"]'}
headers:
accept-ranges: [none]
cache-control: ['no-cache, no-store, must-revalidate']
connection: [close]
content-disposition: [attachment; filename="f.txt"]
content-type: [application/json; charset=UTF-8]
date: ['Mon, 30 Nov 2015 11:56:42 GMT']
expires: ['Fri, 01 Jan 1990 00:00:00 GMT']
pragma: [no-cache]
server: [HTTP server (unknown)]
vary: [Accept-Encoding]
x-content-type-options: [nosniff]
x-frame-options: [SAMEORIGIN]
x-xss-protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1

View file

@ -0,0 +1,25 @@
interactions:
- request:
body: '[{"text": "hello world"}]'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['25']
Content-type: [application/json]
Ocp-Apim-Subscription-Key: [FAKE]
User-Agent: [python-requests/2.19.1]
X-ClientTraceId: [8d466015-2825-495a-8f12-9f88c6e227a9]
method: POST
uri: https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&from=en&to=fr
response:
body: {string: '[{"translations":[{"text":"Salut tout le monde","to":"fr"}]}]'}
headers:
Access-Control-Expose-Headers: [X-RequestId]
Content-Length: ['61']
Content-Type: [application/json; charset=utf-8]
Date: ['Tue, 07 Aug 2018 06:18:08 GMT']
X-Content-Type-Options: [nosniff]
X-RequestId: [TRAN.F3F4.8A3126A3]
status: {code: 200, message: OK}
version: 1

View file

@ -44,6 +44,7 @@ INSTALLED_APPS = [
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'rosetta',
]
@ -52,7 +53,7 @@ if django.VERSION[0:2] >= (1, 7):
LANGUAGE_CODE = "en"
MIDDLEWARE_CLASSES = (
MIDDLEWARE = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
@ -114,3 +115,7 @@ SECRET_KEY = 'empty'
ROSETTA_ENABLE_REFLANG = True
ROSETTA_ENABLE_TRANSLATION_SUGGESTIONS = True
ROSETTA_SHOW_AT_ADMIN_PANEL = True
# fake azure key that matches the one in
# fixtures/vcr_cassettes/test_47_azure_ajax_translation.yaml
AZURE_CLIENT_SECRET = 'FAKE'

View file

@ -4,7 +4,7 @@ from django.contrib import admin
admin.autodiscover()
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^admin/', admin.site.urls),
url(r'^rosetta/', include('rosetta.urls'))
]

28
tox.ini
View file

@ -1,6 +1,9 @@
[tox]
envlist =
{py27,py36}-django{18,19,110,111},
flake8,
{py27,py36}-django111,
py36-django{20,21,22},
py37-django{20,21,22},
gettext,docs
skipsdist = True
@ -15,22 +18,24 @@ setenv =
PYTHONDONTWRITEBYTECODE=1
deps =
django18: Django==1.8.11
django19: Django==1.9.4
django110: Django==1.10
django111: Django==1.11
django111: Django==1.11.*
django20: Django==2.0.*
django21: Django>=2.1a1,<=2.1.99
django22: Django>=2.2a1,<=2.2.99
py27-django{17,18,19,110,111}: python-memcached
py36-django{17,18,19,110,111}: python3-memcached
py36-django{17,18,19,110,111,20,21,22}: python3-memcached
py37-django{20,21,22}: python3-memcached
# py27-django18: pudb
requests
polib>=1.0.6
microsofttranslator>=0.7
polib>=1.1.0
six
goslate
vcrpy
coverage
[testenv:gettext]
basepython = python3
changedir = rosetta/locale/
whitelist_externals =
msgfmt
@ -54,3 +59,10 @@ deps = sphinx
changedir = docs
commands=
sphinx-build -W -b html . _build/html
[testenv:flake8]
basepython = python3
deps = flake8==2.4.1
commands=
flake8 {toxinidir}/rosetta