Merge branch 'feature/i18n' into develop

This commit is contained in:
Christian Cueni 2022-11-08 16:05:18 +01:00
commit 50da271ea4
22 changed files with 177 additions and 81 deletions

View File

@ -7,13 +7,13 @@ log.debug("AppFooter created");
<template>
<footer class="px-8 py-4 bg-gray-200 border-t flex flex-col lg:flex-row">
<div>@2022 VBV</div>
<div class="lg:ml-8">FAQ</div>
<div class="lg:ml-8">Datenschutzbestimmungen</div>
<div class="lg:ml-8">Impressum</div>
<div class="lg:ml-8">{{ $t("footer.faq") }}</div>
<div class="lg:ml-8">{{ $t("footer.dataProtection") }}</div>
<div class="lg:ml-8">{{ $t("footer.imprint") }}</div>
<div class="flex-grow"></div>
<div>VBV_VERSION_BUILD_NUMBER_VBV</div>
<div class="lg:ml-8">Deutsch</div>
<div class="lg:ml-8">Kontakt</div>
<div class="lg:ml-8">{{ $t("footer.contact") }}</div>
</footer>
</template>

View File

@ -32,7 +32,7 @@ const profilePageSlug = route.params["competenceProfilePageSlug"];
{{ criteria.competence_id }} {{ criteria.title }}
</h4>
<p class="hidden lg:block">
Lerneinheit:
{{ $t("general.learningUnit") }}:
<router-link class="link" :to="criteria.learning_unit.frontend_url">
{{ criteria.learning_unit.title }}
</router-link>
@ -40,7 +40,7 @@ const profilePageSlug = route.params["competenceProfilePageSlug"];
</div>
</div>
<p class="lg:hidden mb-2">
Lerneinheit:
{{ $t("general.learningUnit") }}:
<router-link class="link" :to="criteria.learning_unit.frontend_url">
{{ criteria.learning_unit.title }}
</router-link>
@ -50,7 +50,7 @@ const profilePageSlug = route.params["competenceProfilePageSlug"];
class="link"
:to="`/competence/${profilePageSlug}/criteria/${criteria.slug}`"
>
Sich nochmals einschätzen
{{ $t("competences.assessAgain") }}
</router-link>
</span>
</div>

View File

@ -29,8 +29,8 @@ const block = computed(() => {
<div v-if="block">
<LearningContentContainer
:title="learningContent.title"
next-button-text="Abschliessen und weiter"
exit-text="zurück zum Circle"
:next-button-text="$t('learningContent.completeAndContinue')"
:exit-text="$t('general.backToCircle')"
@exit="circleStore.closeLearningContent(props.learningContent)"
@next="circleStore.continueFromLearningContent(props.learningContent)"
>

View File

@ -37,7 +37,7 @@ const emit = defineEmits(["back", "next", "exit"]);
data-cy="cancel-and-back"
@click="$emit('back')"
>
Zurück
{{ $t("general.backCapitalized") }}
</button>
<button
type="button"

View File

@ -141,9 +141,12 @@ const learningSequenceBorderClass = computed(() => {
<span
v-if="continueTranslationKeyTuple[1]"
class="whitespace-nowrap"
>Los geht's
>
{{ $t("general.start") }}
</span>
<span v-else class="whitespace-nowrap">
{{ $t("general.nextStep") }}
</span>
<span v-else class="whitespace-nowrap"> Weiter geht's </span>
</button>
<div class="hidden sm:block"></div>
<div class="w-full sm:w-auto">

View File

@ -44,9 +44,9 @@ function handleBack() {
<template>
<div v-if="learningUnit">
<LearningContentContainer
:title="`Selbsteinschätzung ${learningUnit.title}`"
:exit-text="'zurück zum Circle'"
:next-button-text="'Weiter'"
:title="$t('selfEvaluation.title', { title: learningUnit.title })"
:exit-text="$t('general.backToCircle')"
:next-button-text="$t('general.next')"
:show-back-button="showBackButton"
@exit="circleStore.closeSelfEvaluation(props.learningUnit)"
@next="handleContinue()"
@ -54,14 +54,19 @@ function handleBack() {
>
<div class="container-medium">
<div class="mt-2 lg:mt-8 text-gray-700">
Schritt {{ state.questionIndex + 1 }} von {{ questions.length }}
{{
$t("selfEvaluation.steps", {
current: state.questionIndex + 1,
max: questions.length,
})
}}
</div>
<p class="text-large mt-4">
Überprüfe, ob du in der Lernheinheit
<span class="font-bold">«{{ learningUnit.title }}»</span> alles verstanden
hast.<br />
Lies die folgende Aussage und bewerte sie:
{{ $t("selfEvaluation.instruction[0]") }}
<span class="font-bold">«{{ learningUnit.title }}»</span>
{{ $t("selfEvaluation.instruction[1]") }}<br />
{{ $t("selfEvaluation.instruction[2]") }}
</p>
<div class="mt-4 lg:mt-8 p-6 lg:p-12 border">
@ -80,7 +85,9 @@ function handleBack() {
@click="circleStore.markCompletion(currentQuestion, 'success')"
>
<it-icon-smiley-happy class="w-16 h-16 mr-4"></it-icon-smiley-happy>
<span class="font-bold text-large"> Ja, ich kann das. </span>
<span class="font-bold text-large">
{{ $t("selfEvaluation.yes") }}.
</span>
</button>
<button
class="flex-1 inline-flex items-center text-left p-4 border"
@ -92,12 +99,13 @@ function handleBack() {
@click="circleStore.markCompletion(currentQuestion, 'fail')"
>
<it-icon-smiley-thinking class="w-16 h-16 mr-4"></it-icon-smiley-thinking>
<span class="font-bold text-xl"> Das muss ich nochmals anschauen. </span>
<span class="font-bold text-xl"> {{ $t("selfEvaluation.no") }}. </span>
</button>
</div>
<div class="mt-6 lg:mt-12">
Schau dein Fortschritt in deinem KompetenzNavi: KompetenzNavi öffnen
{{ $t("selfEvaluation.progressText") }}
{{ $t("selfEvaluation.progressLink") }}
</div>
</div>
</div>

View File

@ -4,7 +4,7 @@ import { createI18n } from "vue-i18n";
// https://vue-i18n.intlify.dev/guide/advanced/lazy.html
export const SUPPORT_LOCALES = ["de", "fr", "it"];
export function setupI18n(options = { locale: "de" }) {
export function setupI18n(options = { locale: "de", legacy: false }) {
const i18n = createI18n(options);
setI18nLanguage(i18n, options.locale);
return i18n;

View File

@ -1,3 +1,67 @@
{
"test": "Hallo VBV"
"general": {
"nextStep": "Weiter geht's",
"start": "Los geht's",
"backToLearningPath": "zurück zum Lernpfad",
"backToCircle": "zurück zum Circle",
"next": "Weiter",
"back": "zurück",
"backCapitalized": "@.capitalize:general.back",
"save": "Speichern",
"learningUnit": "Lerneinheit",
"show": "Anschauen"
},
"dashboard": {
"welcome": "Willkommen, {name}"
},
"learningPathPage": {
"welcomeBack": "Willkommen zurück, {name}",
"showListView": "Listenansicht anzeigen",
"nextStep": "Nächster Schritt"
},
"circlePage": {
"duration": "Dauer",
"circleContentBoxTitle": "Das lernst du in diesem Circle."
},
"learningContent": {
"completeAndContinue": "Abschliessen und weiter"
},
"selfEvaluation": {
"title": "Selbsteinschätzung {title}",
"steps": "Schritt {current} von {max}",
"instruction": [
"Überprüfe, ob du in der Lernheinheit",
"alles verstanden hast.",
"Lies die folgende Aussage und bewerte sie:"
],
"yes": "Ja, ich kann das",
"no": "Das muss ich nochmals anschauen",
"progressText": "Schau dein Fortschritt in deinem KompetenzNavi:",
"progressLink": "KompetenzNavi öffnen"
},
"competences": {
"title": "KompetenzNavi",
"lastImprovements": "Letzte verbesserte Kompetenzen",
"showAll": "Alle anschauen",
"assessment": "Einschätzungen",
"notAssessed": "Nicht eingeschätzt",
"assessAgain": "Sich nochmals einschätzen"
},
"mediaLibrary": {
"title": "Mediathek",
"learningMedia": {
"titel": "Lernmedien",
"description": "Finde eine vollständige Liste der Bücher und anderen Medien, auf die im Kurs verwiesen wird."
},
"handlungsfelder": {
"title": "Handlungsfeld | Handlungsfelder",
"description": "Finde alle Ressourcen der Handlungsfelder wie Lernmedien, Links und andere nützliche Informationen."
}
},
"footer": {
"dataProtection": "Datenschutzbestimmungen",
"imprint": "Impressum",
"contact": "Kontakt",
"faq": "FAQ"
}
}

View File

@ -2,37 +2,38 @@ import * as log from "loglevel";
import { createPinia } from "pinia";
import { createApp, markRaw } from "vue";
// import {setupI18n} from './i18n'
import App from "./App.vue";
import { loadLocaleMessages, setupI18n } from "./i18n";
import router from "./router";
import type { Router } from "vue-router";
import "../tailwind.css";
declare module "pinia" {
export interface PiniaCustomProperties {
router: Router;
}
}
if (window.location.href.indexOf("localhost") >= 0) {
log.setLevel("trace");
} else {
log.setLevel("warn");
}
// const i18n = setupI18n()
const i18n = setupI18n();
const app = createApp(App);
// todo: define lang setup
// await loadLocaleMessages(i18n, 'de')
loadLocaleMessages(i18n, "de").then(() => {
app.use(router);
app.use(router);
declare module "pinia" {
export interface PiniaCustomProperties {
router: Router;
}
}
const pinia = createPinia();
pinia.use(({ store }) => {
const pinia = createPinia();
pinia.use(({ store }) => {
store.router = markRaw(router);
});
app.use(pinia);
// app.use(i18n)
});
app.use(pinia);
app.use(i18n);
app.mount("#app");
app.mount("#app");
});

View File

@ -17,7 +17,9 @@ function employer() {
<div class="flex flex-col lg:flex-row">
<main class="grow bg-gray-200 lg:order-2">
<div class="mt-14 container-medium">
<h1 data-cy="welcome-message">Willkommen, {{ userStore.first_name }}</h1>
<h1 data-cy="welcome-message">
{{ $t("dashboard.welcome", { name: userStore.first_name }) }}
</h1>
<div class="mb-14">
<h2 class="mt-12 mb-3">Kurse</h2>
<div class="flex flex-col md:flex-row justify-between">
@ -25,7 +27,7 @@ function employer() {
<h3 class="mb-4">Versicherungsvermittler/in</h3>
<img class="mb-8 block" :src="'/static/icons/demo/vm-lernpfad.svg'" />
<router-link class="btn-blue" to="/learn/versicherungsvermittlerin-lp">
Weiter geht's
{{ $t("general.nextStep") }}
</router-link>
</div>
<div class="bg-white p-6 md:w-[48%]">

View File

@ -53,7 +53,7 @@ const countStatus = computed(() => {
v-if="competenceStore.competenceProfilePage"
class="flex flex-col lg:flex-row items-center justify-between mb-10"
>
<h1>KompetenzNavi</h1>
<h1>{{ $t("competences.title") }}</h1>
<ItDropdownSelect
v-model="competenceStore.selectedCircle"
class="w-full lg:w-96 mt-4 lg:mt-0"
@ -62,7 +62,7 @@ const countStatus = computed(() => {
</div>
<div class="bg-white px-8 py-4 lg:py-8 l mb-4 lg:mb-8">
<div>
<h3 class="pb-4 border-b">Letzte verbesserte Kompetenzen</h3>
<h3 class="pb-4 border-b">{{ $t("competences.lastImprovements") }}</h3>
<ul>
<li
v-for="competence in lastUpdatedCompetences"
@ -85,20 +85,22 @@ const countStatus = computed(() => {
:to="`${competenceStore.competenceProfilePage?.frontend_url}/competences`"
class="btn-text inline-flex items-center pl-0 py-2"
>
<span>Alle anschauen</span>
<span>{{ $t("competences.showAll") }}</span>
<it-icon-arrow-right></it-icon-arrow-right>
</router-link>
</div>
</div>
<div class="bg-white px-8 py-4 lg:py-8 l mb-4 lg:mb-8">
<h3 class="pb-4 lg:pb-0 mb-4 border-b lg:border-0">Einschätzungen</h3>
<h3 class="pb-4 lg:pb-0 mb-4 border-b lg:border-0">
{{ $t("competences.assessment") }}
</h3>
<ul
class="flex flex-col lg:flex-row lg:items-center lg:justify-between lg:gap-8 mb-6"
>
<li
class="inline-block pb-4 lg:pb-0 mb-4 border-b lg:border-b-0 lg:mb-0 lg:border-r flex-1"
>
<h5 class="text-gray-700 mb-4">«Das muss ich nochmals anschauen»</h5>
<h5 class="text-gray-700 mb-4">«{{ $t("selfEvaluation.no") }}»</h5>
<div class="flex flex-row items-center">
<it-icon-smiley-thinking class="w-16 h-16"></it-icon-smiley-thinking>
<p class="text-7xl font-bold inline-block ml-4">{{ countStatus.fail }}</p>
@ -107,7 +109,7 @@ const countStatus = computed(() => {
<li
class="inline-block pb-4 lg:pb-0 mb-4 border-b lg:border-b-0 lg:mb-0 lg:border-r flex-1"
>
<h5 class="text-gray-700 mb-4">«Ja, ich kann das»</h5>
<h5 class="text-gray-700 mb-4">«{{ $t("selfEvaluation.no") }}»</h5>
<div class="flex flex-row items-center">
<it-icon-smiley-happy class="w-16 h-16"></it-icon-smiley-happy>
<p class="text-7xl font-bold inline-block ml-4">
@ -116,7 +118,7 @@ const countStatus = computed(() => {
</div>
</li>
<li class="pb-4 lg:pb-0 border-b lg:border-b-0 lg:mb-0flex-1">
<h5 class="text-gray-700 mb-4">Nicht eingeschätzt</h5>
<h5 class="text-gray-700 mb-4">{{ $t("competences.notAssessed") }}</h5>
<div class="flex flex-row items-center">
<it-icon-smiley-neutral class="w-16 h-16"></it-icon-smiley-neutral>
<p class="text-7xl font-bold inline-block ml-4">
@ -129,7 +131,7 @@ const countStatus = computed(() => {
:to="`${competenceStore.competenceProfilePage?.frontend_url}/criteria`"
class="btn-text inline-flex items-center pl-0 py-2"
>
<span>Alle anschauen</span>
<span>{{ $t("competences.showAll") }}</span>
<it-icon-arrow-right></it-icon-arrow-right>
</router-link>
</div>
@ -139,7 +141,7 @@ const countStatus = computed(() => {
>
<div class="border-b flex flex-row items-center pb-4 mb-4">
<it-icon-smiley-thinking class="w-11 h-11 mr-5"></it-icon-smiley-thinking>
<h3>«Das muss ich nochmals anschauen»</h3>
<h3>«{{ $t("selfEvaluation.no") }}»</h3>
</div>
<ul class="mb-6">
<li
@ -154,7 +156,7 @@ const countStatus = computed(() => {
:to="`${competenceStore.competenceProfilePage?.frontend_url}/criteria`"
class="btn-text inline-flex items-center pl-0 py-2"
>
<span>Alle anschauen</span>
<span>{{ $t("competences.showAll") }}</span>
<it-icon-arrow-right></it-icon-arrow-right>
</router-link>
</div>

View File

@ -18,7 +18,7 @@ const competenceStore = useCompetenceStore();
:to="competenceStore.competenceProfilePage?.frontend_url"
>
<it-icon-arrow-left />
<span>zurück</span>
<span>{{ $t("general.back") }}</span>
</router-link>
</nav>
<div class="flex flex-col lg:flex-row items-center justify-between mb-10">

View File

@ -6,6 +6,7 @@ import type { CourseCompletionStatus } from "@/types";
import * as log from "loglevel";
import type { Ref } from "vue";
import { computed, ref } from "vue";
import { useI18n } from "vue-i18n";
log.debug("CompetencesMainView created");
@ -23,20 +24,22 @@ const shownCriteria = computed(() => {
});
});
const { t } = useI18n();
const mobileMenuItems: MenuItem[] = [
{
id: "fail",
name: "«Das muss ich nochmals anschauen»",
name: `«${t("selfEvaluation.no")}»`,
iconName: "it-icon-smiley-thinking",
},
{
id: "success",
name: "«Ja, ich kann das»",
name: `«${t("selfEvaluation.yes")}»`,
iconName: "it-icon-smiley-happy",
},
{
id: "unknown",
name: "Nicht eingeschätzt",
name: t("competences.notAssessed"),
iconName: "it-icon-smiley-neutral",
},
];

View File

@ -58,8 +58,8 @@ findCriteria();
<div v-if="competencePage" class="absolute top-0 w-full bottom-0 bg-white">
<LearningContentContainer
:title="''"
:exit-text="'zurück'"
:next-button-text="'Speichern'"
:exit-text="$t('general.back')"
:next-button-text="$t('general.save')"
@exit="router.back()"
@next="router.back()"
>
@ -80,7 +80,7 @@ findCriteria();
@click="circleStore.markCompletion(currentQuestion, 'success')"
>
<it-icon-smiley-happy class="w-16 h-16 mr-4"></it-icon-smiley-happy>
<span class="font-bold text-large"> Ja, ich kann das. </span>
<span class="font-bold text-large"> {{ $t("selfEvaluation.yes") }} </span>
</button>
<button
class="flex-1 inline-flex items-center text-left p-4 border"
@ -92,7 +92,7 @@ findCriteria();
@click="circleStore.markCompletion(currentQuestion, 'fail')"
>
<it-icon-smiley-thinking class="w-16 h-16 mr-4"></it-icon-smiley-thinking>
<span class="font-bold text-xl"> Das muss ich nochmals anschauen. </span>
<span class="font-bold text-xl"> {{ $t("selfEvaluation.no") }} </span>
</button>
</div>
</div>

View File

@ -91,14 +91,14 @@ onMounted(async () => {
data-cy="back-to-learning-path-button"
>
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
<span class="inline">zurück zum Lernpfad</span>
<span class="inline">{{ $t("general.backToLearningPath") }}</span>
</router-link>
<h1 class="text-blue-dark text-4xl lg:text-6xl" data-cy="circle-title">
{{ circleStore.circle?.title }}
</h1>
<div class="mt-2">Dauer: {{ duration }}</div>
<div class="mt-2">{{ $t("circlePage.duration") }}: {{ duration }}</div>
<div class="w-full mt-8">
<CircleDiagram></CircleDiagram>
@ -120,7 +120,9 @@ onMounted(async () => {
<div class="hidden lg:block">
<div class="block border mt-8 p-6">
<h3 class="text-blue-dark">Das lernst du in diesem Circle.</h3>
<h3 class="text-blue-dark">
{{ $t("circlePage.circleContentBoxTitle") }}
</h3>
<div class="leading-relaxed mt-4">
{{ circleStore.circle?.description }}
</div>

View File

@ -64,7 +64,7 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
@click="learningPathStore.page = 'OVERVIEW'"
>
<it-icon-list />
Listenansicht anzeigen
{{ $t("learningPathPage.showListView") }}
</button>
</div>
<LearningPathDiagram
@ -82,14 +82,16 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
class="bg-white p-4 lg:mb-16 flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-gray-500 justify-start"
>
<div class="p-2 lg:p-8 flex-auto">
<h2>Willkommmen zurück, {{ userStore.first_name }}</h2>
<h2>
{{ $t("learningPathPage.welcomeBack", { name: userStore.first_name }) }}
</h2>
<p class="mt-4 text-xl"></p>
</div>
<div
v-if="learningPathStore.learningPath.nextLearningContent"
class="p-4 lg:p-8 flex-2"
>
Nächster Schritt
{{ $t("learningPathPage.nextStep") }}
<h3>
{{
learningPathStore.learningPath.nextLearningContent.parentCircle.title
@ -106,9 +108,9 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
translate
>
<span v-if="createContinueUrl(learningPathStore.learningPath)[1]">
Los geht's
{{ $t("general.start") }}
</span>
<span v-else>Weiter geht's</span>
<span v-else>{{ $t("general.nextStep") }}</span>
</router-link>
</div>
</div>

View File

@ -71,12 +71,14 @@ function hasMoreItemsForType<T>(itemType: MediaBlockType, items: T[]) {
:to="(backLink as string)"
>
<it-icon-arrow-left />
<span>zurück</span>
<span>{{ $t("general.back") }}</span>
</router-link>
</nav>
<div class="flex md:flex-col lg:flex-row justify-between">
<div class="lg:w-6/12">
<h3 class="font-normal text-large text-gray-900 mb-3">Handlungsfeld</h3>
<h3 class="font-normal text-large text-gray-900 mb-3">
{{ $t("mediaLibrary.handlungsfelder.title", 1) }}
</h3>
<h1 class="mb-4 lg:mb-8" data-cy="hf-title">{{ mediaCategory.title }}</h1>
<p class="text-large">{{ mediaCategory.introduction_text }}</p>
</div>

View File

@ -27,7 +27,7 @@ watch(dropdownSelected, (newValue) =>
<template>
<div class="container-large">
<div class="flex flex-col lg:flex-row items-center justify-between mb-10 mt-6">
<h1>Handlungsfelder</h1>
<h1>{{ $t("mediaLibrary.handlungsfelder.title", categories.length) }}</h1>
<!-- <ItDropdownSelect v-model="dropdownSelected" :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
</div>
<div v-if="mediaStore.mediaLibraryPage">

View File

@ -28,27 +28,26 @@ watch(dropdownSelected, (newValue) =>
<template>
<div class="container-large">
<div class="flex flex-col lg:flex-row items-center justify-between mb-12 mt-6">
<h1>Mediathek</h1>
<h1>{{ $t("mediaLibrary.title") }}</h1>
<!-- <ItDropdownSelect-->
<!-- v-model="dropdownSelected"-->
<!-- :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
</div>
<OverviewCard
v-if="mediaStore.mediaLibraryPage"
title="Handlungsfelder"
call2-action="Anschauen"
:title="$t('mediaLibrary.handlungsfelder.title', 2)"
:call2-action="$t('general.show')"
:link="`${mediaStore.mediaLibraryPage.frontend_url}/category`"
description="Finde alle Ressourcen der Handlungsfelder wie Lernmedien, Links und andere nützliche Informationen."
:description="$t('mediaLibrary.handlungsfelder.description')"
icon="handlungsfelder-overview"
class="mb-6"
>
</OverviewCard>
></OverviewCard>
<OverviewCard
v-if="mediaStore.mediaLibraryPage && generalCategory"
title="Lernmedien"
call2-action="Anschauen"
:title="$t('mediaLibrary.learningMedia.titel')"
:call2-action="$t('general.show')"
:link="`${generalCategory.frontend_url}/media`"
description="Finde eine vollständige Liste der Bücher und anderen Medien, auf die im Kurs verwiesen wird."
:description="$t('mediaLibrary.learningMedia.description')"
icon="lernmedien-overview"
class="mb-6"
>

View File

@ -33,6 +33,11 @@ export default defineConfig(({ mode }) => {
// ]
}),
],
define: {
__VUE_I18N_FULL_INSTALL__: true,
__VUE_I18N_LEGACY_API__: false,
__INTLIFY_PROD_DEVTOOLS__: false,
},
server: {
port: 5173,
hmr: { port: 5173 },

2
requests.http Normal file
View File

@ -0,0 +1,2 @@
# Logout
POST http://localhost:8000/core/logout/

View File

@ -452,6 +452,7 @@ CORS_URLS_REGEX = r"^/api/.*$"
CSP_DEFAULT_SRC = [
"'self'",
"'unsafe-inline'",
"'unsafe-eval'",
"ws://localhost:5173",
"ws://127.0.0.1:5173",
"localhost:8000",