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

This commit is contained in:
Marco Bonetti 2023-12-04 20:38:39 +01:00
commit 14e9f44901
19 changed files with 103 additions and 77 deletions

View file

@ -9,7 +9,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: [3.8, 3.9, "3.10"]
python-version: ["3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v1

15
CHANGES
View file

@ -1,14 +1,23 @@
Version History
===============
Version 0.9.9 (unreleased)
--------------------------
Version 0.10.0
--------------
* Fix link to polib. (#277, thanks @gamboz)
* Deepl: use the PRO API endpoint when using a PRO API key. (#278, thanks @nullcode)
* Limit supported versions to Django 4.2 and 5.0, using Python 3.9, 3.10, 3.11 and 3.12
Version 0.9.9
-------------
* Use packaged jQuery instead of the CDN version. (#274, thanks @stephanema1)
* Test test_47_azure_ajax_translation: avoid DNS lookup for better isolation. Should fix #233
* Adds Chinese (Simplified) translation. (#266 thanks @chenluyong)
* Test against Django 4.1a
* Test against Django 4.1 and 4.2
* Limit supported versions to Django 3.2 and later, using Python 3.8, 3.9 and 3.10
* Proxy Deepl translations suggestions through the back-end to avoid CORS issues. (#271 thanks @rafaelromon, @biermeester and @matthiask)
* Format code with pre-commit
* Replace case sensitivity check with setting (#273 thanks @patroqueeet)
Version 0.9.8

View file

@ -1,4 +1,4 @@
Copyright (c) 2008-2010 Marco Bonetti
Copyright (c) 2008-2023 Marco Bonetti
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation

View file

@ -16,7 +16,12 @@ Rosetta is a `Django <http://www.djangoproject.com/>`_ application that facilita
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.9.0, django-rosetta requires Django 1.11 or later, as of version 0.9.6, django-rosetta requires Django 2.2 or later, and as of version 0.9.9 django-rosetta supports Django 3.2 or later.
Note:
* As of version 0.9.0, django-rosetta requires Django 1.11 or later
* As of version 0.9.6, django-rosetta requires Django 2.2 or later
* As of version 0.9.9 django-rosetta supports Django 3.2 or later
* As of version 0.10.0, django-rosetta requires Django 4.2 or later
********
Features

View file

@ -60,7 +60,7 @@ master_doc = "index"
# General information about the project.
project = "Django Rosetta"
copyright = "2008 2021 Marco Bonetti and contributors"
copyright = "2008 2023 Marco Bonetti and contributors"
author = "Marco Bonetti"
@ -73,7 +73,7 @@ release = get_version()
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = "en"
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:

View file

@ -27,6 +27,7 @@ Rosetta can be configured via the following parameters, to be defined in your pr
* ``ROSETTA_LOGIN_URL``: Use this if you want to override the login URL for rosetta. Defaults to ``settings.LOGIN_URL``.
* ``ROSETTA_LANGUAGES``: List of languages that Rosetta will offer to translate. This is useful when you wish to translate a language that is not yet defined in ``settings.LANGUAGES``. Defaults to ``settings.LANGUAGES``.
* ``ROSETTA_SHOW_OCCURRENCES``: Determines whether occurrences (where the original text appears) should be shown next to the translations for context. Defaults to ``True``.
* ``ROSETTA_CASE_SENSITIVE_FILESYSTEM``: Overrides auto-detection of case sensitive OS. Defaults to ``None`` which enables auto-detection. Useful when running case sensitive OS (e.g. Ubuntu) in docker on case insensitive OS (e.g. MacOS).

View file

@ -38,4 +38,4 @@ By default Rosetta hides its own catalog files in the file selection interface (
Acknowledgments
***************
* Rosetta uses the excellent `polib <https://bitbucket.org/izi/polib>`_ library to parse and handle gettext files.
* Rosetta uses the excellent `polib <https://pypi.org/project/polib/>`_ library to parse and handle gettext files.

View file

@ -6,7 +6,7 @@ order_by_type = true
known_django = "django"
known_django_third_party = "django_*"
known_first_party = "apps,rosetta"
sections = "FUTURE,STDLIB,THIRDPARTY,DJANGO,DJANGO_THIRD_PARTY,REST_FRAMEWORK,FIRSTPARTY,LOCALFOLDER"
sections = "FUTURE,STDLIB,THIRDPARTY,DJANGO,DJANGO_THIRD_PARTY,FIRSTPARTY,LOCALFOLDER"
skip_glob= "**/migrations/**"

View file

@ -1,12 +1,4 @@
try:
import django
if django.VERSION[:3] <= (3, 2, 0):
default_app_config = "rosetta.apps.RosettaAppConfig"
except ImportError:
pass
VERSION = (0, 9, 9)
VERSION = (0, 10, 0)
def get_version(limit=3):

View file

@ -71,11 +71,18 @@ def find_pos(lang, project_apps=True, django_apps=False, third_party_apps=False)
)
)
case_sensitive_file_system = True
tmphandle, tmppath = tempfile.mkstemp()
if os.path.exists(tmppath.upper()):
# Case insensitive file system.
case_sensitive_file_system = False
# is OS case sensitive? settings preferred over auto detection
case_sensitive_file_system = getattr(
settings, "ROSETTA_CASE_SENSITIVE_FILESYSTEM", None
)
# in case of no settings, attempt auto detection
if case_sensitive_file_system is None:
case_sensitive_file_system = True
tmphandle, tmppath = tempfile.mkstemp()
if os.path.exists(tmppath.upper()):
# Case insensitive file system.
case_sensitive_file_system = False
# django/locale
if django_apps:

View file

@ -11,7 +11,7 @@
{% include 'rosetta/css/rosetta.css' %}
</style>
{% block extra_styles %}{% endblock %}
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="{% static "admin/js/vendor/jquery/jquery.min.js" %}"></script>
<script type="text/javascript">
{% include 'rosetta/js/rosetta.js' %}
</script>

View file

@ -93,7 +93,7 @@ $(document).ready(function() {
if($(this).val()) {
$('.alert', $(this).parents('tr')).remove();
var RX = /%(?:\([^\s\)]*\))?[sdf]|\{[\w\d_]+?\}/g,
origs=$(this).parents('tr').find('.original>.message').html().match(RX),
origs=$(this).parents('tr').find('.original span').html().match(RX),
trads=$(this).val().match(RX),
error = $('<span class="alert">Unmatched variables</span>');

View file

@ -2,6 +2,7 @@ import filecmp
import hashlib
import os
import shutil
from unittest import mock
from urllib.parse import urlencode
import vcr
@ -17,6 +18,7 @@ from django.urls import resolve, reverse
from django.utils.encoding import force_bytes
from rosetta import views
from rosetta.poutil import find_pos
from rosetta.signals import entry_changed, post_save
from rosetta.storage import get_storage
@ -382,14 +384,16 @@ class RosettaTestCase(TestCase):
# Post a translation, it should have properly wrapped lines
data = {
"m_bb9d8fe6159187b9ea494c1b313d23d4": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean "
"commodo ligula eget dolor. Aenean massa. Cum sociis natoque "
"penatibus et magnis dis parturient montes, nascetur ridiculus "
"mus. Donec quam felis, ultricies nec, pellentesque eu, pretium "
"quis, sem. Nulla consequat massa quis enim. Donec pede justo, "
"fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, "
"rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum "
"felis eu pede mollis pretium."
"m_bb9d8fe6159187b9ea494c1b313d23d4": (
"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean "
"commodo ligula eget dolor. Aenean massa. Cum sociis natoque "
"penatibus et magnis dis parturient montes, nascetur ridiculus "
"mus. Donec quam felis, ultricies nec, pellentesque eu, pretium "
"quis, sem. Nulla consequat massa quis enim. Donec pede justo, "
"fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, "
"rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum "
"felis eu pede mollis pretium."
)
}
r = self.client.post(self.xx_form_url, data)
with open(self.dest_file, "r") as po_file:
@ -762,8 +766,9 @@ class RosettaTestCase(TestCase):
msg_hashes = message_hashes()
data = {msg_hashes["String 1"]: "Translation 1"}
self.client.post(self.xx_form_url, data)
po_file_hash_before, mo_file_hash_before = file_hash(po_file), file_hash(
mo_file
po_file_hash_before, mo_file_hash_before = (
file_hash(po_file),
file_hash(mo_file),
)
# Make a change to the translations
@ -1053,6 +1058,23 @@ class RosettaTestCase(TestCase):
resp = self.client.get(reverse("admin:index"))
self.assertNotContains(resp, "rosetta-content-main")
@mock.patch("rosetta.poutil.os.path.exists")
def test_273_override_case_sensitivity(self, path_mock):
path_mock.exists.return_value = False
# no setting
find_pos("en")
path_mock.assert_called_with(mock.ANY)
path_mock.reset_mock()
with override_settings(ROSETTA_CASE_SENSITIVE_FILESYSTEM=False):
find_pos("en")
path_mock.isfile.assert_not_called()
path_mock.reset_mock()
with override_settings(ROSETTA_CASE_SENSITIVE_FILESYSTEM=True):
find_pos("en")
path_mock.isfile.assert_not_called()
# Stubbed access control function
def no_access(user):

View file

@ -48,8 +48,13 @@ def translate(text, from_language, to_language):
def translate_by_deepl(text, to_language, auth_key):
if auth_key.lower().endswith(":fx"):
endpoint = "https://api-free.deepl.com"
else:
endpoint = "https://api.deepl.com"
r = requests.post(
"https://api-free.deepl.com/v2/translate",
f"{endpoint}/v2/translate",
headers={"Authorization": f"DeepL-Auth-Key {auth_key}"},
data={
"target_lang": to_language.upper(),

View file

@ -27,12 +27,12 @@ urlpatterns = [
name="rosetta-file-list",
),
re_path(
r"^files/(?P<po_filter>[\w-]+)/(?P<lang_id>[\w\-_\.]+)/(?P<idx>\d+)/$",
r"^files/(?P<po_filter>[\w-]+)/(?P<lang_id>[\w\-_\.@]+)/(?P<idx>\d+)/$",
views.TranslationFormView.as_view(),
name="rosetta-form",
),
re_path(
r"^files/(?P<po_filter>[\w-]+)/(?P<lang_id>[\w\-_\.]+)/(?P<idx>\d+)/download/$",
r"^files/(?P<po_filter>[\w-]+)/(?P<lang_id>[\w\-_\.@]+)/(?P<idx>\d+)/download/$",
views.TranslationFileDownload.as_view(),
name="rosetta-download-file",
),

View file

@ -52,19 +52,16 @@ setup(
"Topic :: Software Development :: Localization",
"Topic :: Software Development :: Internationalization",
"Framework :: Django",
"Framework :: Django :: 3.0",
"Framework :: Django :: 3.1",
"Framework :: Django :: 3.2",
"Framework :: Django :: 4.0",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Framework :: Django :: 4.2",
"Framework :: Django :: 5.0",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
],
include_package_data=True,
zip_safe=False,
install_requires=["Django >= 2.2", "requests >= 2.1.0", "polib >= 1.1.0"],
install_requires=["Django >= 4.2", "requests >= 2.30.0", "polib >= 1.1.0"],
tests_require=["tox", "vcrpy"],
cmdclass={"test": Tox},
)

View file

@ -17,7 +17,7 @@ interactions:
User-Agent:
- python-requests/2.26.0
method: POST
uri: https://api-free.deepl.com/v2/translate
uri: https://api.deepl.com/v2/translate
response:
body: {string: '{"translations": [{"detected_source_language": "EN", "text": "Salut tout le monde"}]}'}
headers:

View file

@ -1,8 +1,6 @@
import os
import sys
import django
SITE_ID = 1
@ -17,22 +15,13 @@ DATABASES = {
}
}
if django.VERSION[:3] >= (3, 2, 0):
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "127.0.0.1:11211",
"KEY_PREFIX": "ROSETTA_TEST",
}
}
else:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"LOCATION": "127.0.0.1:11211",
"KEY_PREFIX": "ROSETTA_TEST",
}
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "127.0.0.1:11211",
"KEY_PREFIX": "ROSETTA_TEST",
}
}
# CACHES = {'default': {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'}}
@ -52,11 +41,9 @@ INSTALLED_APPS = [
"django.contrib.sites",
"django.contrib.messages",
"rosetta",
"rosetta.tests.test_app.apps.TestAppConfig",
]
if django.VERSION[0:2] >= (1, 7):
INSTALLED_APPS.append("rosetta.tests.test_app.apps.TestAppConfig")
LANGUAGE_CODE = "en"
MIDDLEWARE = (
@ -84,8 +71,6 @@ SILENCED_SYSTEM_CHECKS = ["translation.E002"]
LOCALE_PATHS = [os.path.join(PROJECT_PATH, "locale")]
SOUTH_TESTS_MIGRATE = False
FIXTURE_DIRS = (os.path.join(PROJECT_PATH, "fixtures"),)
STATIC_URL = "/static/"
ROOT_URLCONF = "testproject.urls"
@ -112,6 +97,8 @@ TEMPLATES = [
}
]
USE_TZ = True
STATIC_URL = "/static/"
# SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"

15
tox.ini
View file

@ -1,16 +1,18 @@
[tox]
envlist =
flake8,
py{38,39,310}-django{32,40,41},
py{38,39,310}-django42,
py{310,311,312}-django50,
gettext,
docs
[gh-actions]
python =
3.10: py310-django32, py310-django40, py310-django41
3.9: py39-django32, py39-django40, py39-django41
3.8: py38-django32, py38-django40, py38-django41
3.12: py312-django50
3.11: py311-django42, py311-django50
3.10: py310-django42, py310-django50
3.9: py39-django42
skipsdist = True
@ -27,9 +29,8 @@ setenv =
PYTHONDONTWRITEBYTECODE=1
deps =
django32: Django>=3.2,<=3.2.99
django40: Django>=4.0,<4.1
django41: Django>=4.1a,<4.2
django42: Django>=4.2a,<4.3
django50: Django>=5.0,<5.1
pymemcache
requests