mirror of
https://github.com/pretix/pretix.git
synced 2026-05-04 15:04:03 +00:00
Add language translation score to selector widget (#3603)
Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
@@ -44,6 +44,7 @@ from django.forms.utils import from_current_timezone
|
||||
from django.urls import reverse
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.text import format_lazy
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_scopes.forms import SafeModelMultipleChoiceField
|
||||
@@ -51,6 +52,7 @@ from django_scopes.forms import SafeModelMultipleChoiceField
|
||||
from pretix.helpers.hierarkey import clean_filename
|
||||
|
||||
from ...base.forms import I18nModelForm
|
||||
from ...helpers.i18n import get_language_score
|
||||
from ...helpers.images import (
|
||||
IMAGE_EXTS, validate_uploaded_file_for_valid_image,
|
||||
)
|
||||
@@ -300,18 +302,43 @@ class SlugWidget(forms.TextInput):
|
||||
|
||||
|
||||
class MultipleLanguagesWidget(forms.CheckboxSelectMultiple):
|
||||
template_name = 'pretixcontrol/multi_languages_select.html'
|
||||
option_template_name = 'pretixcontrol/multi_languages_widget.html'
|
||||
|
||||
def sort(self):
|
||||
self.choices = sorted(self.choices, key=lambda l: (
|
||||
def filter_and_sort(choices, languages, cond=True):
|
||||
return sorted(
|
||||
[c for c in choices if (c[0] in languages) == cond],
|
||||
key=lambda c: str(c[1])
|
||||
)
|
||||
self.choices = (
|
||||
(
|
||||
0 if l[0] in settings.LANGUAGES_OFFICIAL
|
||||
else (
|
||||
1 if l[0] not in settings.LANGUAGES_INCUBATING
|
||||
else 2
|
||||
)
|
||||
), str(l[1])
|
||||
))
|
||||
'',
|
||||
filter_and_sort(self.choices, settings.LANGUAGES_OFFICIAL)
|
||||
),
|
||||
(
|
||||
(
|
||||
_('Community translations'),
|
||||
format_lazy(
|
||||
_('These translations are not maintained by the pretix team. We cannot vouch for their correctness '
|
||||
'and new or recently changed features might not be translated and will show in English instead. '
|
||||
'You can <a href="{translate_url}" target="_blank">help translating</a>.'),
|
||||
translate_url='https://translate.pretix.eu'
|
||||
),
|
||||
'fa fa-group'
|
||||
),
|
||||
filter_and_sort(self.choices, settings.LANGUAGES_OFFICIAL.union(settings.LANGUAGES_INCUBATING), False)
|
||||
),
|
||||
(
|
||||
(
|
||||
_('Development only'),
|
||||
_('These translations are still in progress. These languages can currently only be selected on development '
|
||||
'installations of pretix, not in production.'),
|
||||
'fa fa-flask text-danger'
|
||||
),
|
||||
filter_and_sort(self.choices, settings.LANGUAGES_INCUBATING)
|
||||
)
|
||||
)
|
||||
|
||||
def options(self, name, value, attrs=None):
|
||||
self.sort()
|
||||
@@ -325,6 +352,8 @@ class MultipleLanguagesWidget(forms.CheckboxSelectMultiple):
|
||||
opt = super().create_option(name, value, label, selected, index, subindex, attrs)
|
||||
opt['official'] = value in settings.LANGUAGES_OFFICIAL
|
||||
opt['incubating'] = value in settings.LANGUAGES_INCUBATING
|
||||
base_score = get_language_score("de")
|
||||
opt['score'] = round(get_language_score(value) / base_score * 100)
|
||||
return opt
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{% with id=widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %} class="{{ widget.attrs.class }} checkbox">{% for group, options, index in widget.optgroups %}{% if group %}
|
||||
<div class="checkbox-group"><div class="checkbox-group-legend"><span>{% if group.2 %}<i class="{{ group.2 }}" aria-hidden="true"></i> {% endif %}{{ group.0 }}</span></div><p class="text-muted">{{ group.1|safe }}</p>{% endif %}{% for option in options %}<div class="checkbox">
|
||||
{% include option.template_name with widget=option %}</div>{% endfor %}{% if group %}
|
||||
</div>{% endif %}{% endfor %}
|
||||
</div>{% endwith %}
|
||||
@@ -5,13 +5,12 @@
|
||||
{% include "django/forms/widgets/input.html" %}
|
||||
{% if widget.wrap_label %}
|
||||
{{ widget.label }}
|
||||
{% if widget.incubating %}
|
||||
<span class="label label-danger" data-toggle="tooltip" title="{% trans "The translation for this language is still in progress. This language can currently only be selected on development installations of pretix, not in production." %}">
|
||||
{% trans "Translation in development" %}
|
||||
</span>
|
||||
{% elif not widget.official %}
|
||||
<span class="label label-warning" data-toggle="tooltip" title="{% trans "This translation is not maintained by the pretix team. We cannot vouch for its correctness and new or recently changed features might not be translated and will show in English instead. You can help translating at translate.pretix.eu." %}">
|
||||
{% trans "Unofficial translation" %}
|
||||
{% if widget.incubating or not widget.official %}
|
||||
|
||||
<span class="label label-{% if widget.score > 50 %}success{% else %}{% if widget.score > 30 %}info{% else %}default{% endif %}{% endif %}"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "This percentage of texts is translated across all parts of the system including most plugins. Even a low value might be enough if you only use specific features. Untranslated texts will show up in English." %}">
|
||||
{{ widget.score }} %
|
||||
</span>
|
||||
{% endif %}
|
||||
</label>
|
||||
|
||||
@@ -19,12 +19,18 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import gettext as gettext_module
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from functools import lru_cache
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.utils import translation
|
||||
from django.utils.formats import get_format
|
||||
from django.utils.translation import to_locale
|
||||
from django.utils.translation.trans_real import TranslationCatalog
|
||||
|
||||
date_conversion_to_moment = {
|
||||
'%a': 'ddd',
|
||||
@@ -156,3 +162,63 @@ def get_moment_locale(locale=None):
|
||||
|
||||
def i18ncomp(query):
|
||||
return json.dumps(str(query))[1:-1]
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_language_score(locale):
|
||||
"""
|
||||
For a given language code, return a numeric score on how well-translated the language is. The score
|
||||
is an integer greater than 1 and can be arbitrarily high, so it's only useful for comparing with
|
||||
other languages.
|
||||
|
||||
Note that there is no valid score for "en", since it's technically not "translated".
|
||||
"""
|
||||
catalog = None
|
||||
app_configs = reversed(apps.get_app_configs())
|
||||
|
||||
for app in app_configs:
|
||||
# Filter out all third-party apps by looking for the pretix name and for valid pretix plugins
|
||||
if not app.name.startswith("pretix") and not hasattr(app, 'PretixPluginMeta'):
|
||||
continue
|
||||
if hasattr(app, 'PretixPluginMeta'):
|
||||
# Filter out invisible plugins and plugins only available to some users
|
||||
p = app.PretixPluginMeta
|
||||
if not getattr(p, 'visible', True) or hasattr(app, 'is_available'):
|
||||
continue
|
||||
localedir = os.path.join(app.path, "locale")
|
||||
if os.path.exists(localedir):
|
||||
try:
|
||||
translation = gettext_module.translation(
|
||||
domain="django",
|
||||
localedir=localedir,
|
||||
languages=[to_locale(locale)],
|
||||
fallback=False,
|
||||
)
|
||||
except:
|
||||
continue
|
||||
if catalog is None:
|
||||
catalog = TranslationCatalog(translation)
|
||||
else:
|
||||
catalog.update(translation)
|
||||
|
||||
# Add pretix' main translation folder as well as installation-specific translation folders
|
||||
for localedir in reversed(settings.LOCALE_PATHS):
|
||||
try:
|
||||
translation = gettext_module.translation(
|
||||
domain="django",
|
||||
localedir=localedir,
|
||||
languages=[to_locale(locale)],
|
||||
fallback=False,
|
||||
)
|
||||
except:
|
||||
continue
|
||||
if catalog is None:
|
||||
catalog = TranslationCatalog(translation)
|
||||
else:
|
||||
catalog.update(translation)
|
||||
|
||||
if not catalog:
|
||||
score = 1
|
||||
else:
|
||||
score = len(list(catalog.items())) or 1
|
||||
return score
|
||||
|
||||
@@ -116,6 +116,22 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
margin-top: 1.25em;
|
||||
margin-bottom: 1.25em;
|
||||
}
|
||||
.checkbox-group-legend {
|
||||
margin-bottom: .65em;
|
||||
border-bottom: 1px solid #ccc;
|
||||
font-weight: bold;
|
||||
}
|
||||
.checkbox-group-legend > span {
|
||||
background: white;
|
||||
padding: .5em;
|
||||
padding-left: 0;
|
||||
position: relative;
|
||||
top: .35em;
|
||||
}
|
||||
|
||||
.alert {
|
||||
text-align: left;
|
||||
|
||||
Reference in New Issue
Block a user