mirror of
https://github.com/Hopiu/django-rosetta.git
synced 2026-04-18 20:21:02 +00:00
Merge branch 'develop' of github.com:mbi/django-rosetta into pr/189
This commit is contained in:
commit
41b8a2e790
37 changed files with 1870 additions and 1190 deletions
3
.pep8
Normal file
3
.pep8
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[flake8]
|
||||
ignore = E501
|
||||
exclude = south_migrations,migrations,.venv_*,docs
|
||||
18
.travis.yml
18
.travis.yml
|
|
@ -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
54
CHANGES
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
--------
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
---------------
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
VERSION = (0, 7, 13)
|
||||
VERSION = (0, 9, 1)
|
||||
default_app_config = "rosetta.apps.RosettaAppConfig"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
from django.db import models
|
||||
# Create your models here.
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
{% block pagetitle %}{{block.super}} - {% trans "Language selection" %}{% endblock %}
|
||||
|
||||
{% block breadcumbs %}
|
||||
<div><a href="{% url 'rosetta-pick-file' %}">{% trans "Home" %}</a> › {% trans "Language selection" %}</div>
|
||||
<div><a href="{% url 'rosetta-file-list' po_filter=po_filter %}">{% trans "Home" %}</a> › {% 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> </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>
|
||||
|
|
@ -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> ›
|
||||
<a href="{% url 'rosetta-file-list' po_filter=po_filter %}">{% trans "Home" %}</a> ›
|
||||
{{ rosetta_i18n_lang_name }} ›
|
||||
{{ rosetta_i18n_app|title }} ›
|
||||
{% 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 }}&msg_filter=untranslated">{% trans "Untranslated only" %}</a></li>
|
||||
<li {% if rosetta_i18n_filter == 'translated' %}class="active"{% endif %}><a href="?{{ filter_query_string_base }}&msg_filter=translated">{% trans "Translated only" %}</a></li>
|
||||
<li {% if rosetta_i18n_filter == 'fuzzy' %}class="active"{% endif %}><a href="?{{ filter_query_string_base }}&msg_filter=fuzzy">{% trans "Fuzzy only" %}</a></li>
|
||||
<li {% if rosetta_i18n_filter == 'all' %}class="active"{% endif %}><a href="?{{ filter_query_string_base }}&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="#">… ({% 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 %}&query={{query}}{% endif %}">{{i}}</a>
|
||||
<a href="?{{ pagination_query_string_base }}&page={{i}}">{{i}}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
@ -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(/>/g,'>').replace(/</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(/</g, '<').replace(/>/g, '>'));
|
||||
trans.val(response.text[0].replace(/<br>/g, '\n').replace(/<\/?code>/g, '').replace(/</g, '<').replace(/>/g, '>'));
|
||||
a.hide();
|
||||
} else {
|
||||
a.text(response);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
from .tests import *
|
||||
from .tests import * # NOQA
|
||||
|
|
|
|||
38
rosetta/tests/django.po.issue186.template
Normal file
38
rosetta/tests/django.po.issue186.template
Normal 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"
|
||||
171
rosetta/tests/django.po.test44.template
Normal file
171
rosetta/tests/django.po.test44.template
Normal 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
|
|
@ -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',
|
||||
),
|
||||
]
|
||||
|
|
|
|||
1093
rosetta/views.py
1093
rosetta/views.py
File diff suppressed because it is too large
Load diff
14
setup.py
14
setup.py
|
|
@ -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
4
testproject/coverage.sh
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
coverage run --rcfile .coveragerc manage.py test --failfast rosetta
|
||||
coverage xml
|
||||
coverage html
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
28
tox.ini
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue