Skip to content

Commit

Permalink
Improve translation handling in JavaScript and TypeScript (#2036)
Browse files Browse the repository at this point in the history
* Introduce i18n for JS/TS using Django's javascript catalog

* Extract sortable_form.js.html to sortable_form.js

---------

Co-authored-by: Richard Ebeling <dev@richardebeling.de>
  • Loading branch information
hansegucker and richardebeling authored Nov 6, 2023
1 parent de8abc2 commit 431077a
Show file tree
Hide file tree
Showing 21 changed files with 185 additions and 82 deletions.
8 changes: 8 additions & 0 deletions evap/development/management/commands/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@ class Command(BaseCommand):
def handle(self, *args, **options):
self.stdout.write('Executing "manage.py makemessages --locale=de --ignore=node_modules/*"')
call_command("makemessages", "--locale=de", "--ignore=node_modules/*")
call_command(
"makemessages",
"--domain=djangojs",
"--extension=js,ts",
"--locale=de",
"--ignore=node_modules/*",
"--ignore=evap/static/js/*.min.js",
)
2 changes: 2 additions & 0 deletions evap/evaluation/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@

{% include 'footer.html' %}

<script src="{% url 'javascript-catalog' %}"></script>

<script type="text/javascript" src="{% static 'js/jquery-2.1.3.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/tom-select.complete.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/plugins/jquery.formset.js' %}"></script>
Expand Down
4 changes: 3 additions & 1 deletion evap/evaluation/templates/evap_evaluation_edit_js.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{% load static %}

{% if editable %}
{% include 'sortable_form_js.html' %}
<script src="{% static 'js/sortable_form.js' %}"></script>
<script type="text/javascript">
rowChanged = function(row) {
name = $(row.find("select[id$=-contributor]")).find(":selected").text();
Expand Down
2 changes: 1 addition & 1 deletion evap/evaluation/templates/notebook.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ <h5 class="card-title m-0">
{% csrf_token %}
{{ notebook_form.notes }}
<div class="d-flex justify-content-center pt-3">
<button class="btn btn-primary" type="submit" data-errormessage="{% trans 'The server is not responding.' %}">
<button class="btn btn-primary" type="submit">
<span class="visible-if-ready">{% trans 'Save' %}</span>
<span class="visible-if-sending">{% trans 'Sending...' %}</span>
<span class="visible-if-successful">{% trans 'Saved successfully' %}</span>
Expand Down
62 changes: 0 additions & 62 deletions evap/evaluation/templates/sortable_form_js.html

This file was deleted.

15 changes: 14 additions & 1 deletion evap/evaluation/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,20 @@
from model_bakery import baker

from evap.evaluation.models import Evaluation, Question, QuestionType, UserProfile
from evap.evaluation.tests.tools import WebTestWith200Check, create_evaluation_with_responsible_and_editor
from evap.evaluation.tests.tools import (
WebTestWith200Check,
create_evaluation_with_responsible_and_editor,
store_ts_test_asset,
)


class RenderJsTranslationCatalog(WebTest):
url = reverse("javascript-catalog")

def render_pages(self):
# Not using render_pages decorator to manually create a single (special) javascript file
content = self.app.get(self.url).content
store_ts_test_asset("catalog.js", content)


@override_settings(PASSWORD_HASHERS=["django.contrib.auth.hashers.MD5PasswordHasher"])
Expand Down
21 changes: 13 additions & 8 deletions evap/evaluation/tests/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@ def let_user_vote_for_evaluation(user, evaluation, create_answers=False):
RatingAnswerCounter.objects.bulk_update(rac_by_contribution_question.values(), ["count"])


def store_ts_test_asset(relative_path: str, content) -> None:
absolute_path = os.path.join(settings.STATICFILES_DIRS[0], "ts", "rendered", relative_path)

os.makedirs(os.path.dirname(absolute_path), exist_ok=True)

with open(absolute_path, "wb") as file:
file.write(content)


def render_pages(test_item):
"""Decorator which annotates test methods which render pages.
The containing class is expected to include a `url` attribute which matches a valid path.
Expand All @@ -94,19 +103,15 @@ def render_pages(test_item):
The value is a byte string of the page content."""

@functools.wraps(test_item)
def decorator(self):
def decorator(self) -> None:
pages = test_item(self)

static_directory = settings.STATICFILES_DIRS[0]

url = getattr(self, "render_pages_url", self.url)
# Remove the leading slash from the url to prevent that an absolute path is created
directory = os.path.join(static_directory, "ts", "rendered", url[1:])
os.makedirs(directory, exist_ok=True)

for name, content in pages.items():
with open(os.path.join(directory, f"{name}.html"), "wb") as html_file:
html_file.write(content)
# Remove the leading slash from the url to prevent that an absolute path is created
path = os.path.join(url[1:], f"{name}.html")
store_ts_test_asset(path, content)

return decorator

Expand Down
30 changes: 30 additions & 0 deletions evap/locale/de/LC_MESSAGES/djangojs.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# EvaP translation
# This file is distributed under the same license as the EvaP project.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: EvaP\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-10-30 17:26+0100\n"
"PO-Revision-Date: 2023-10-30 17:26+0100\n"
"Last-Translator: Johannes Wolf <janno42@posteo.de>\n"
"Language-Team: Johannes Wolf (janno42)\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.3.2\n"

#: evap/static/js/notebook.js:30 evap/static/ts/src/notebook.ts:39
msgid "The server is not responding."
msgstr ""

#: evap/static/js/sortable_form.js:19
msgid "Delete"
msgstr ""

#: evap/static/js/sortable_form.js:20
msgid "add another"
msgstr ""
4 changes: 3 additions & 1 deletion evap/staff/templates/staff_course_type_index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{% extends 'staff_base.html' %}

{% load static %}

{% block breadcrumb %}
{{ block.super }}
<li class="breadcrumb-item">{% trans 'Course types' %}</li>
Expand Down Expand Up @@ -71,7 +73,7 @@
{% endblock %}

{% block additional_javascript %}
{% include 'sortable_form_js.html' %}
<script src="{% static 'js/sortable_form.js' %}"></script>

<script type="text/javascript">
rowChanged = function(row) {
Expand Down
4 changes: 3 additions & 1 deletion evap/staff/templates/staff_degree_index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{% extends 'staff_base.html' %}

{% load static %}

{% block breadcrumb %}
{{ block.super }}
<li class="breadcrumb-item">{% trans 'Degrees' %}</li>
Expand Down Expand Up @@ -67,7 +69,7 @@
{% endblock %}

{% block additional_javascript %}
{% include 'sortable_form_js.html' %}
<script src="{% static 'js/sortable_form.js' %}"></script>

<script type="text/javascript">
rowChanged = function(row) {
Expand Down
4 changes: 3 additions & 1 deletion evap/staff/templates/staff_faq_index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{% extends 'staff_base.html' %}

{% load static %}

{% block breadcrumb %}
{{ block.super }}
<li class="breadcrumb-item">{% trans 'FAQ Sections' %}</li>
Expand Down Expand Up @@ -57,7 +59,7 @@
{% endblock %}

{% block additional_javascript %}
{% include 'sortable_form_js.html' %}
<script src="{% static 'js/sortable_form.js' %}"></script>

<script type="text/javascript">
rowChanged = function(row) {
Expand Down
4 changes: 3 additions & 1 deletion evap/staff/templates/staff_faq_section.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{% extends 'staff_base.html' %}

{% load static %}

{% block breadcrumb %}
{{ block.super }}
<li class="breadcrumb-item"><a href="{% url 'staff:faq_index' %}">{% trans 'FAQ Sections' %}</a></li>
Expand Down Expand Up @@ -57,7 +59,7 @@
{% endblock %}

{% block additional_javascript %}
{% include 'sortable_form_js.html' %}
<script src="{% static 'js/sortable_form.js' %}"></script>

<script type="text/javascript">
rowChanged = function(row) {
Expand Down
4 changes: 3 additions & 1 deletion evap/staff/templates/staff_questionnaire_form.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{% extends 'staff_questionnaire_base.html' %}

{% load static %}

{% block content %}
{{ block.super }}
{% if not editable %}
Expand Down Expand Up @@ -79,7 +81,7 @@ <h5 class="card-title">{% trans 'Questions' %}</h5>

{% block additional_javascript %}
{% if editable %}
{% include 'sortable_form_js.html' %}
<script src="{% static 'js/sortable_form.js' %}"></script>
<script type="text/javascript">
rowChanged = function(row) {
nameDe = $(row.find('textarea[id$=-text_de]')).val();
Expand Down
4 changes: 2 additions & 2 deletions evap/staff/templates/staff_semester_export.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% extends 'staff_semester_base.html' %}

{% load evaluation_filters %}
{% load evaluation_filters static %}

{% block breadcrumb %}
{{ block.super }}
Expand Down Expand Up @@ -72,7 +72,7 @@ <h3>{% trans 'Export' %} {{ semester.name }}</h3>
{% endblock %}

{% block additional_javascript %}
{% include 'sortable_form_js.html' %}
<script src="{% static 'js/sortable_form.js' %}"></script>

<script type="text/javascript">
rowChanged = function(row) {
Expand Down
2 changes: 1 addition & 1 deletion evap/staff/templates/staff_text_answer_warnings.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
{% endblock %}

{% block additional_javascript %}
{% include 'sortable_form_js.html' %}
<script src="{% static 'js/sortable_form.js' %}"></script>

{{ text_answer_warnings|text_answer_warning_trigger_strings|json_script:'text-answer-warnings' }}

Expand Down
1 change: 1 addition & 0 deletions evap/static/js/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ignore the typescript output, but still track the libraries
*.js
!*.min.js
!sortable_form.js
60 changes: 60 additions & 0 deletions evap/static/js/sortable_form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
function makeFormSortable(tableId, prefix, rowChanged, rowAdded, tolerance, removeAsButton, usesTemplate) {

function applyOrdering() {
$(document).find('tr').each(function(i) {
if (rowChanged($(this))) {
$(this).find('input[id$=-order]').val(i);
}
else {
// if the row is empty (has no text in the input fields) set the order to -1 (default),
// so that the one extra row doesn't change its initial value
$(this).find('input[id$=-order]').val(-1);
}
});
}

$('#' + tableId + ' tbody tr').formset({
prefix: prefix,
deleteCssClass: removeAsButton ? 'btn btn-danger btn-sm' : 'delete-row',
deleteText: removeAsButton ? '<span class="fas fa-trash"></span>' : gettext('Delete'),
addText: gettext('add another'),
added: function(row) {
row.find('input[id$=-order]').val(row.parent().children().length);

// We have to empty the formset, otherwise sometimes old contents from
// invalid forms are copied (#644).
// Checkboxes with 'data-keep' need to stay checked.
row.find("input:checkbox:not([data-keep]),:radio").removeAttr("checked");

row.find("input:text,textarea").val("");

row.find("select").each(function(){
$(this).find('option:selected').removeAttr("selected");
$(this).find('option').first().attr("selected", "selected");
});

//Check the first item in every button group
row.find(".btn-group").each(function() {
var inputs = $(this).find("input");
$(inputs[0]).prop("checked", true);
});

//Remove all error messages
row.find(".error-label").remove();

rowAdded(row);
},
formTemplate: (usesTemplate ? ".form-template" : null)
});

new Sortable($('#' + tableId + " tbody").get(0), {
handle: ".fa-up-down",
draggable: ".sortable",
scrollSensitivity: 70
});

$('form').submit(function() {
applyOrdering();
return true;
});
}
3 changes: 2 additions & 1 deletion evap/static/ts/src/notebook.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "./translation.js";
import { unwrap, assert, selectOrError } from "./utils.js";

const NOTEBOOK_LOCALSTORAGE_KEY = "evap_notebook_open";
Expand Down Expand Up @@ -35,7 +36,7 @@ class NotebookFormLogic {
.catch(() => {
this.notebook.setAttribute("data-state", "ready");
submitter.disabled = false;
alert(submitter.dataset.errormessage);
alert(window.gettext("The server is not responding."));
});
};

Expand Down
Loading

0 comments on commit 431077a

Please sign in to comment.