Merged in feature/translations (pull request #202)

Feature/translations
This commit is contained in:
Daniel Egger 2023-09-08 14:45:40 +00:00
commit 10a8e5ef04
15 changed files with 101 additions and 47 deletions

View File

@ -67,7 +67,7 @@ const clickLink = (to: string | undefined) => {
data-cy="medialibrary-link"
@click="clickLink(`${courseSession?.media_library_url}`)"
>
{{ $t("mediaLibrary.title") }}
{{ $t("a.Mediathek") }}
</button>
</li>
</ul>

View File

@ -79,7 +79,9 @@ const assignment = computed(
>
<div class="flex items-center text-gray-900">
<it-icon-assignment class="h-6 w-6"></it-icon-assignment>
<div class="ml-2">Geleitete Fallarbeit: {{ assignment?.title }}</div>
<div class="ml-2">
{{ $t("a.Geleitete Fallarbeit") }}: {{ assignment?.title }}
</div>
</div>
<button
type="button"
@ -98,7 +100,7 @@ const assignment = computed(
<div class="h-full overflow-y-auto bg-white sm:w-1/2">
<!-- Left part content goes here -->
<div class="p-10" data-cy="student-submission">
<h3>Ergebnisse</h3>
<h3>{{ $t("a.Ergebnisse") }}</h3>
<div class="my-6 flex items-center">
<img

View File

@ -155,7 +155,7 @@ function finishButtonEnabled() {
@click="emit('close')"
>
<span class="flex items-center">
Bewertung abschliessen
{{ $t("a.Bewertung abschliessen") }}
<it-icon-check class="ml-2 h-6 w-6"></it-icon-check>
</span>
</button>

View File

@ -48,28 +48,36 @@ async function startEvaluation() {
<template>
<div>
<div class="mb-4">
{{ props.assignmentUser.first_name }} {{ props.assignmentUser.last_name }} hat die
Ergebnisse am
{{ dayjs(props.assignmentCompletion.submitted_at).format("DD.MM.YYYY") }} um
{{ dayjs(props.assignmentCompletion.submitted_at).format("HH.mm") }} Uhr
abgegeben.
{{
$t("assignment.x hat die Ergebnisse am y um z Uhr abgegeben", {
x: props.assignmentUser.first_name + " " + props.assignmentUser.last_name,
y: dayjs(props.assignmentCompletion.submitted_at).format("DD.MM.YYYY"),
z: dayjs(props.assignmentCompletion.submitted_at).format("HH.mm"),
})
}}
</div>
<h3>Bewertung</h3>
<h3>{{ $t("a.Bewertung") }}</h3>
<p v-if="props.dueDate" class="my-4">
Du musst die Bewertung bis am {{ props.dueDate.format("DD.MM.YYYY") }} um
{{ props.dueDate.format("HH.mm") }} Uhr abschliessen und freigeben.
{{
$t(
"assignment.Du musst die Bewertung bis am x um y Uhr abschliessen und freigeben",
{
x: props.dueDate.format("DD.MM.YYYY"),
y: props.dueDate.format("HH.mm"),
}
)
}}
</p>
<p class="my-4">
Die Gesamtpunktzahl und die daraus resultierende Note wird auf Grund des
hinterlegeten Beurteilungsinstrument berechnet.
{{ $t("assignment.evaluationInstrumentDescriptionText") }}
</p>
<p class="my-4">
<a :href="props.assignment.evaluation_document_url" class="link" target="_blank">
Beurteilungsinstrument anzeigen
{{ $t("a.Beurteilungsinstrument anzeigen") }}
</a>
</p>
@ -84,16 +92,16 @@ async function startEvaluation() {
props.assignmentCompletion.completion_status === 'EVALUATION_IN_PROGRESS'
"
>
Bewertung fortsetzen
{{ $t("a.Bewertung fortsetzen") }}
</span>
<span
v-else-if="
props.assignmentCompletion.completion_status === 'EVALUATION_SUBMITTED'
"
>
Bewertung ansehen
{{ $t("a.Bewertung ansehen") }}
</span>
<span v-else>Bewertung starten</span>
<span v-else>{{ $t("a.Bewertung starten") }}</span>
</button>
</div>
</div>

View File

@ -102,17 +102,16 @@ const evaluationUser = computed(() => {
<h3 v-if="evaluationUser && props.showEvaluationUser" class="mb-6">
Bewertung von {{ evaluationUser.first_name }} {{ evaluationUser.last_name }}
</h3>
<h3 v-else class="mb-6">Bewertung Freigabe</h3>
<h3 v-else class="mb-6">{{ $t("a.Bewertung Freigabe") }}</h3>
<section class="mb-6 border p-6">
<div class="text-lg font-bold">Note: {{ grade }}</div>
<div class="text-gray-900">
Gesamtpunktezahl {{ userPoints }} / {{ maxPoints }}
{{ $t("a.Gesamtpunktzahl") }} {{ userPoints }} / {{ maxPoints }}
</div>
<p class="my-4">
Die Gesamtpunktzahl und die daraus resultierende Note wird auf Grund des
hinterlegeten Beurteilungsinstrument berechnet.
{{ $t("assignment.evaluationInstrumentDescriptionText") }}
</p>
<p class="my-4">
@ -121,7 +120,7 @@ const evaluationUser = computed(() => {
class="link"
target="_blank"
>
Beurteilungsinstrument anzeigen
{{ $t("a.Beurteilungsinstrument anzeigen") }}
</a>
</p>
@ -138,7 +137,7 @@ const evaluationUser = computed(() => {
</div>
<div v-else>
<button class="btn-primary text-large" @click="submitEvaluation()">
Bewertung freigeben
{{ $t("a.Bewertung freigeben") }}
</button>
</div>

View File

@ -96,6 +96,10 @@ const loadAttendanceData = async () => {
}
};
function editAgain() {
state.attendanceSaved = false;
}
onMounted(() => {
log.debug("AttendanceCheckPage mounted");
loadAttendanceData();
@ -153,8 +157,11 @@ watch(
</div>
<div v-else class="self-center">
<p class="text-base">
{{ $t("Die Anwesenheit wurde definitiv bestätigt.") }}
{{ $t("a.Die Anwesenheit wurde definitiv bestätigt") }}
</p>
<button class="btn-link link" @click="editAgain()">
{{ $t("a.Erneut bearbeiten") }}
</button>
</div>
</div>

View File

@ -13,7 +13,7 @@ const emit = defineEmits(["closemodal"]);
<template>
<ItFullScreenModal :show="show" @closemodal="emit('closemodal')">
<div v-if="circle" class="container-medium">
<h2 data-cy="lc-title">Überblick: Circle «{{ circle.title }}»</h2>
<h2 data-cy="lc-title">{{ $t("a.Übersicht") }}: Circle «{{ circle.title }}»</h2>
<!-- eslint-disable vue/no-v-html -->
<div

View File

@ -4,7 +4,7 @@ import { useCircleStore } from "@/stores/circle";
import type { LearningContent } from "@/types";
import * as log from "loglevel";
import type { Ref } from "vue";
import { onMounted, ref, watch } from "vue";
import { getCurrentInstance, onMounted, onUpdated, ref, watch } from "vue";
log.debug("LearningContentView created");
@ -52,6 +52,24 @@ onMounted(async () => {
);
await loadLearningContent();
});
onUpdated(() => {
const vueInstance = getCurrentInstance();
if (vueInstance) {
// VBV-489: open external links in new tab
const rootElement: HTMLElement = vueInstance.proxy?.$el;
const anchors = rootElement.querySelectorAll("a");
anchors.forEach((anchor: HTMLAnchorElement) => {
if (
/^https?:\/\//i.test(anchor.href) &&
!anchor.href.includes(window.location.hostname)
) {
anchor.setAttribute("target", "_blank");
anchor.setAttribute("rel", "noopener noreferrer");
}
});
}
});
</script>
<template>

View File

@ -74,7 +74,7 @@ const step = useRouteQuery("step");
props.assignment.evaluation_document_url
"
>
<h3 class="mb-4 mt-8">{{ $t("assignment.assessmentTitle") }}</h3>
<h3 class="mb-4 mt-8">{{ $t("a.Bewertung") }}</h3>
<p
v-if="props.assignment.evaluation_description"
class="default-wagtail-rich-text text-large"

View File

@ -69,7 +69,7 @@ async function startTest() {
class="btn-primary inline-flex items-center"
@click="startTest()"
>
Test starten
{{ $t("edoniqTest.startTest") }}
<it-icon-external-link class="it-icon ml-2 h-5 w-5"></it-icon-external-link>
</button>
</div>

View File

@ -69,8 +69,8 @@ onUnmounted(() => {
>
<LearningContentMultiLayout
:current-step="questionIndex"
:sub-title="$t('selfEvaluation.title')"
:title="$t('selfEvaluation.title', { title: learningUnit.title })"
:sub-title="$t('a.Selbsteinschätzung')"
:title="`${$t('a.Selbsteinschätzung')}: ${learningUnit.title}`"
icon="it-icon-lc-learning-module"
:steps-count="questions.length"
:show-next-button="showNextButton"
@ -101,7 +101,7 @@ onUnmounted(() => {
>
<it-icon-smiley-happy class="mr-4 h-16 w-16"></it-icon-smiley-happy>
<span class="text-large font-bold">
{{ $t("selfEvaluation.yes") }}.
{{ $t("selfEvaluation.yes") }}
</span>
</button>
<button
@ -117,7 +117,7 @@ onUnmounted(() => {
<it-icon-smiley-thinking
class="mr-4 h-16 w-16"
></it-icon-smiley-thinking>
<span class="text-xl font-bold">{{ $t("selfEvaluation.no") }}.</span>
<span class="text-xl font-bold">{{ $t("selfEvaluation.no") }}</span>
</button>
</div>

View File

@ -44,7 +44,7 @@ export function learningContentTypeData(
icon: "it-icon-lc-learning-module",
};
case "learnpath.LearningContentMediaLibrary":
return { title: t("mediaLibrary.title"), icon: "it-icon-lc-media-library" };
return { title: t("a.Mediathek"), icon: "it-icon-lc-media-library" };
case "learnpath.LearningContentVideo":
return { title: t("learningContentTypes.video"), icon: "it-icon-lc-video" };
case "learnpath.LearningContentEdoniqTest":

View File

@ -16,9 +16,10 @@ else
fi
# Use sentry for supercronic only in prod* environments
if [[ $IT_APP_ENVIRONMENT == prod* ]]; then
sed -i "s|command=/usr/local/bin/supercronic /app/supercronic_crontab|command=/usr/local/bin/supercronic /app/supercronic_crontab -sentry-dsn '$IT_SENTRY_DSN'|" /app/supervisord.conf
fi
# FIXME: does not seem to work
#if [[ $IT_APP_ENVIRONMENT == prod* ]]; then
# sed -i "s|command=/usr/local/bin/supercronic /app/supercronic_crontab|command=/usr/local/bin/supercronic /app/supercronic_crontab -sentry-dsn '$IT_SENTRY_DSN'|" /app/supervisord.conf
#fi
# Set the command to run supervisord
/home/django/.local/bin/supervisord -c /app/supervisord.conf

View File

@ -74,7 +74,7 @@ class FeedbackSummaryApiTestCase(FeedbackApiBaseTestCase):
notification = Notification.objects.first()
self.assertEqual(notification.recipient, expert)
self.assertEqual(
notification.verb, f"New feedback for circle {basis_circle.title}"
notification.verb, f"Feedback abgeschickt für Circle «{basis_circle.title}»"
)
self.assertEqual(
notification.target_url,

View File

@ -1,6 +1,5 @@
from __future__ import annotations
from gettext import gettext
from typing import TYPE_CHECKING
import structlog
@ -33,9 +32,12 @@ class NotificationService:
def send_assignment_submitted_notification(
cls, recipient: User, sender: User, assignment_completion: AssignmentCompletion
):
verb = gettext(
"%(sender)s hat die geleitete Fallarbeit «%(assignment_title)s» abgegeben."
) % {
texts = {
"de": "%(sender)s hat die geleitete Fallarbeit «%(assignment_title)s» abgegeben.",
"fr": "%(sender)s a soumis l'étude de cas dirigée «%(assignment_title)s».",
"it": "%(sender)s ha consegnato il caso di studio guidato «%(assignment_title)s».",
}
verb = texts.get(recipient.language, "de") % {
"sender": sender.get_full_name(),
"assignment_title": assignment_completion.assignment.title,
}
@ -60,9 +62,12 @@ class NotificationService:
assignment_completion: AssignmentCompletion,
target_url: str,
):
verb = gettext(
"%(sender)s hat die geleitete Fallarbeit «%(assignment_title)s» bewertet."
) % {
texts = {
"de": "%(sender)s hat die geleitete Fallarbeit «%(assignment_title)s» bewertet.",
"fr": "%(sender)s a évalué l'étude de cas dirigée «%(assignment_title)s».",
"it": "%(sender)s ha valutato il caso di studio guidato «%(assignment_title)s».",
}
verb = texts.get(recipient.language, "de") % {
"sender": sender.get_full_name(),
"assignment_title": assignment_completion.assignment.title,
}
@ -85,7 +90,14 @@ class NotificationService:
recipient: User,
feedback_response: FeedbackResponse,
):
verb = f"New feedback for circle {feedback_response.circle.title}"
texts = {
"de": "Feedback abgeschickt für Circle «%(circle_title)s»",
"fr": "Feedback envoyé pour le cercle «%(circle_title)s»",
"it": "Feedback inviato per il cerchio «%(circle_title)s»",
}
verb = texts.get(recipient.language, "de") % {
"circle_title": feedback_response.circle.title,
}
return cls._send_notification(
recipient=recipient,
@ -104,9 +116,16 @@ class NotificationService:
recipient: User,
attendance_course: CourseSessionAttendanceCourse,
):
texts = {
"de": "Erinnerung: Bald findet ein Präsenzkurs statt",
"fr": "Rappel: Un cours de présence aura lieu bientôt.",
"it": "Promemoria: Un corso di presenza avrà luogo presto.",
}
verb = texts.get(recipient.language, "de")
return cls._send_notification(
recipient=recipient,
verb="Erinnerung: Bald findet ein Präsenzkurs statt",
verb=verb,
notification_category=NotificationCategory.INFORMATION,
notification_trigger=NotificationTrigger.ATTENDANCE_COURSE_REMINDER,
target_url=attendance_course.learning_content.get_frontend_url(),