🌐 Added some missing translations

This commit is contained in:
Mawoka
2023-04-19 18:37:46 +02:00
parent a0b02ec9c3
commit b079f72c23
26 changed files with 221 additions and 93 deletions
+7 -7
View File
@@ -132,7 +132,7 @@
{#if selected_question + 1 === quiz_data.questions.length && ((timer_res === '0' && question_results !== null) || quiz_data?.questions?.[selected_question]?.type === QuizQuestionType.SLIDE)} {#if selected_question + 1 === quiz_data.questions.length && ((timer_res === '0' && question_results !== null) || quiz_data?.questions?.[selected_question]?.type === QuizQuestionType.SLIDE)}
{#if JSON.stringify(final_results) === JSON.stringify([null])} {#if JSON.stringify(final_results) === JSON.stringify([null])}
<button on:click={get_final_results} class="admin-button" <button on:click={get_final_results} class="admin-button"
>Get final results >{$t('admin_page.get_final_results')}
</button> </button>
{/if} {/if}
{:else if timer_res === '0' || selected_question === -1} {:else if timer_res === '0' || selected_question === -1}
@@ -142,7 +142,7 @@
set_question_number(selected_question + 1); set_question_number(selected_question + 1);
}} }}
class="admin-button" class="admin-button"
>Next Question ({selected_question + 2}) >{$t('admin_page.next_question', { question: selected_question + 2 })}
</button> </button>
{/if} {/if}
{#if question_results === null && selected_question !== -1} {#if question_results === null && selected_question !== -1}
@@ -152,11 +152,11 @@
set_question_number(selected_question + 1); set_question_number(selected_question + 1);
}} }}
class="admin-button" class="admin-button"
>Next Question ({selected_question + 2}) >{$t('admin_page.next_question', { question: selected_question + 2 })}
</button> </button>
{:else} {:else}
<button on:click={get_question_results} class="admin-button" <button on:click={get_question_results} class="admin-button"
>Show results >{$t('admin_page.show_results')}
</button> </button>
{/if} {/if}
{/if} {/if}
@@ -167,11 +167,11 @@
set_question_number(selected_question + 1); set_question_number(selected_question + 1);
}} }}
class="admin-button" class="admin-button"
>Next Question ({selected_question + 2}) >{$t('admin_page.next_question', { question: selected_question + 2 })}
</button> </button>
{:else} {:else}
<button on:click={show_solutions} class="admin-button" <button on:click={show_solutions} class="admin-button"
>Stop time and show solutions >{$t('admin_page.stop_time_and_solutions')}
</button> </button>
{/if} {/if}
{:else} {:else}
@@ -262,7 +262,7 @@
</div> </div>
{:else} {:else}
<div class="flex justify-center"> <div class="flex justify-center">
<p class="text-2xl">Enter your answer into the input field!</p> <p class="text-2xl">{$t('admin_page.enter_answer_into_field')}</p>
</div> </div>
{/if} {/if}
{/if} {/if}
+5 -2
View File
@@ -11,6 +11,9 @@
import SettingsCard from '$lib/editor/settings-card.svelte'; import SettingsCard from '$lib/editor/settings-card.svelte';
import QuizCard from '$lib/editor/card.svelte'; import QuizCard from '$lib/editor/card.svelte';
import Spinner from './Spinner.svelte'; import Spinner from './Spinner.svelte';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
let schemaInvalid = false; let schemaInvalid = false;
let yupErrorMessage = ''; let yupErrorMessage = '';
@@ -152,10 +155,10 @@
</p> </p>
{/if} {/if}
<button <button
class="pr-2 align-middle bg-purple-400 pl-2 ml-auto whitespace-nowrap disabled:opacity-60 rounded-br-lg" class="pr-2 align-middle bg-[#B07156] pl-2 ml-auto whitespace-nowrap disabled:opacity-60 rounded-br-lg"
disabled={schemaInvalid} disabled={schemaInvalid}
> >
<span>Save</span> <span>{$t('words.save')}</span>
<svg <svg
class="w-6 h-6 inline-block" class="w-6 h-6 inline-block"
fill="none" fill="none"
@@ -80,7 +80,7 @@
type="text" type="text"
class="border-b-2 border-dotted w-5/6 text-center rounded-lg bg-transparent" class="border-b-2 border-dotted w-5/6 text-center rounded-lg bg-transparent"
style="background-color: {answer.color ?? 'transparent'}" style="background-color: {answer.color ?? 'transparent'}"
placeholder="Empty..." placeholder={$t('editor.empty')}
/> />
<button <button
type="button" type="button"
@@ -141,7 +141,7 @@
type="text" type="text"
class="border-b-2 border-dotted w-5/6 text-center rounded-lg bg-transparent" class="border-b-2 border-dotted w-5/6 text-center rounded-lg bg-transparent"
style="background-color: {answer.color ?? 'transparent'}" style="background-color: {answer.color ?? 'transparent'}"
placeholder="Empty..." placeholder={$t('editor.empty')}
/> />
<input <input
class="rounded-lg p-1 border-black border" class="rounded-lg p-1 border-black border"
@@ -74,7 +74,7 @@
bind:value={answer.answer} bind:value={answer.answer}
type="text" type="text"
class="border-b-2 border-dotted w-5/6 text-center rounded-lg bg-transparent" class="border-b-2 border-dotted w-5/6 text-center rounded-lg bg-transparent"
placeholder="Empty..." placeholder={$t('editor.empty')}
/> />
<button <button
type="button" type="button"
@@ -79,7 +79,7 @@
type="text" type="text"
class="border-b-2 border-dotted w-5/6 text-center rounded-lg" class="border-b-2 border-dotted w-5/6 text-center rounded-lg"
style="background-color: {answer.color ?? 'transparent'}" style="background-color: {answer.color ?? 'transparent'}"
placeholder="Empty..." placeholder={$t('editor.empty')}
/> />
<input <input
class="rounded-lg p-1 border-black border" class="rounded-lg p-1 border-black border"
+6
View File
@@ -42,9 +42,15 @@
.toString() .toString()
.slice(0, 3); .slice(0, 3);
} }
};
const set_unique = () => {
unique = {}; unique = {};
}; };
$: correctTimeInput(data.questions[selected_question].time); $: correctTimeInput(data.questions[selected_question].time);
$: {
selected_question;
set_unique();
}
/* /*
if (typeof data.questions[selected_question].type !== QuizQuestionType) { if (typeof data.questions[selected_question].type !== QuizQuestionType) {
console.log(data.questions[selected_question].type !== QuizQuestionType.ABCD || data.questions[selected_question].type !== QuizQuestionType.RANGE) console.log(data.questions[selected_question].type !== QuizQuestionType.ABCD || data.questions[selected_question].type !== QuizQuestionType.RANGE)
+1 -1
View File
@@ -180,7 +180,7 @@
</div> </div>
</div> </div>
<div class="flex justify-center pt-10"> <div class="flex justify-center pt-10">
<h3>Background-Image</h3> <h3>{$t('editor.bg_image')}</h3>
</div> </div>
<div class="w-full flex justify-center -mt-8"> <div class="w-full flex justify-center -mt-8">
{#if data.background_image} {#if data.background_image}
+14 -10
View File
@@ -9,6 +9,8 @@
import { reach } from 'yup'; import { reach } from 'yup';
import { ABCDQuestionSchema, dataSchema } from '../yupSchemas'; import { ABCDQuestionSchema, dataSchema } from '../yupSchemas';
import { createTippy } from 'svelte-tippy'; import { createTippy } from 'svelte-tippy';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
export let data: EditorData; export let data: EditorData;
export let selected_question = -1; export let selected_question = -1;
@@ -78,7 +80,7 @@
{#if data.title} {#if data.title}
{@html data.title} {@html data.title}
{:else} {:else}
<i>No title...</i> <i>{$t('editor.no_title')}</i>
{/if} {/if}
</p> </p>
</div> </div>
@@ -121,7 +123,7 @@
d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/> />
</svg> </svg>
<span>Public</span> <span>{$t('words.public')}</span>
{:else} {:else}
<svg <svg
class="w-5 h-5 inline-block" class="w-5 h-5 inline-block"
@@ -137,7 +139,7 @@
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
/> />
</svg> </svg>
<span>Private</span> <span>{$t('words.private')}</span>
{/if} {/if}
</button> </button>
</div> </div>
@@ -190,7 +192,7 @@
class:dark:text-black={index === selected_question} class:dark:text-black={index === selected_question}
> >
{#if question.question === ''} {#if question.question === ''}
<span class="italic text-gray-500">No title...</span> <span class="italic text-gray-500">{$t('editor.no_title')}</span>
{:else} {:else}
{@html question.question} {@html question.question}
{/if} {/if}
@@ -222,10 +224,11 @@
'answer' 'answer'
).isValidSync(answer.answer)} ).isValidSync(answer.answer)}
use:tippy={{ use:tippy={{
content: answer.answer === '' ? 'Empty...' : answer.answer content:
answer.answer === '' ? $t('editor.empty') : answer.answer
}} }}
>{#if answer.answer === ''} >{#if answer.answer === ''}
<i>Empty...</i> <i>{$t('editor.empty')}</i>
{:else} {:else}
{answer.answer} {answer.answer}
{/if}</span {/if}</span
@@ -252,10 +255,11 @@
'answer' 'answer'
).isValidSync(answer.answer)} ).isValidSync(answer.answer)}
use:tippy={{ use:tippy={{
content: answer.answer === '' ? 'Empty...' : answer.answer content:
answer.answer === '' ? $t('editor.empty') : answer.answer
}} }}
>{#if answer.answer === ''} >{#if answer.answer === ''}
<i>Empty...</i> <i>{$t('editor.empty')}</i>
{:else} {:else}
{answer.answer} {answer.answer}
{/if}</span {/if}</span
@@ -280,7 +284,7 @@
data.questions = [...data.questions, { ...empty_question }]; data.questions = [...data.questions, { ...empty_question }];
}} }}
> >
<span class="w-full text-center">Question</span> <span class="w-full text-center">{$t('words.question')}</span>
<svg <svg
class="w-5/6 m-auto" class="w-5/6 m-auto"
fill="none" fill="none"
@@ -303,7 +307,7 @@
data.questions = [...data.questions, { ...empy_slide }]; data.questions = [...data.questions, { ...empy_slide }];
}} }}
> >
<span class="w-full text-center">Slide</span> <span class="w-full text-center">{$t('words.slide')}</span>
<svg <svg
class="w-5/6 m-auto" class="w-5/6 m-auto"
fill="none" fill="none"
@@ -7,7 +7,9 @@
import { ElementTypes } from '$lib/quiz_types'; import { ElementTypes } from '$lib/quiz_types';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
export let selected_element; export let selected_element;
const keybinding_list = { const keybinding_list = {
t: ElementTypes.Text, t: ElementTypes.Text,
@@ -40,22 +42,22 @@
shortcut: string; shortcut: string;
}> = [ }> = [
{ {
name: 'Headline', name: $t('editor.slide.headline'),
description: 'A bold text for headlines', description: $t('editor.slide.headline_description'),
type: ElementTypes.Headline, type: ElementTypes.Headline,
icon: undefined, icon: undefined,
shortcut: 'h' shortcut: 'h'
}, },
{ {
name: 'Text', name: $t('editor.slide.text'),
description: 'Smaller longer text', description: $t('editor.slide.text_description'),
type: ElementTypes.Text, type: ElementTypes.Text,
icon: undefined, icon: undefined,
shortcut: 't' shortcut: 't'
}, },
{ {
name: 'Rectangle', name: $t('editor.slide.rectangle'),
description: 'Just a rectangle', description: $t('editor.slide.rectangle_description'),
type: ElementTypes.Rectangle, type: ElementTypes.Rectangle,
icon: undefined, icon: undefined,
shortcut: 'r' shortcut: 'r'
+11 -3
View File
@@ -48,7 +48,8 @@
"community_driven_content": "ClassQuiz hängt von der Community ab, die ClassQuiz mit Spenden, Feature-Requests, Übersetzungen und mehr versorgt! Du kannst auch ein Teil der ClassQuiz-Community werden!", "community_driven_content": "ClassQuiz hängt von der Community ab, die ClassQuiz mit Spenden, Feature-Requests, Übersetzungen und mehr versorgt! Du kannst auch ein Teil der ClassQuiz-Community werden!",
"download_quizzes_content": "Quiz können als einzige Datei heruntergeladen werden und jederzeit importiert werden, sodass du auch einfach auf eine andere Instanz von ClassQuiz umziehen kannst!", "download_quizzes_content": "Quiz können als einzige Datei heruntergeladen werden und jederzeit importiert werden, sodass du auch einfach auf eine andere Instanz von ClassQuiz umziehen kannst!",
"community_driven": "Von der Community betrieben", "community_driven": "Von der Community betrieben",
"download_quizzes": "Quiz herunterladen" "download_quizzes": "Quiz herunterladen",
"how_does_classquiz_work": "Wie funktioniert ClassQuiz überhaupt?"
}, },
"overview_page": { "overview_page": {
"created_at": "Erstellt am", "created_at": "Erstellt am",
@@ -161,7 +162,8 @@
"score": "Punkte", "score": "Punkte",
"results": "Ergebnisse", "results": "Ergebnisse",
"note": "Notiz", "note": "Notiz",
"player_plural": "Spieler" "player_plural": "Spieler",
"slide": "Folie"
}, },
"editor": { "editor": {
"time_in_seconds": "Zeit in Sekunden", "time_in_seconds": "Zeit in Sekunden",
@@ -170,7 +172,10 @@
"add_new_question": "Neue Frage hinzufügen", "add_new_question": "Neue Frage hinzufügen",
"delete_question": "Frage löschen", "delete_question": "Frage löschen",
"delete_answer": "Antwort löschen", "delete_answer": "Antwort löschen",
"not_all_links_imgur_links": "Nicht alle Links sind Imgur-Links!" "not_all_links_imgur_links": "Nicht alle Links sind Imgur-Links!",
"no_title": "Kein Titel...",
"empty": "Leer...",
"bg_image": "Hintergrundbild"
}, },
"import": { "import": {
"need_help": "", "need_help": "",
@@ -279,5 +284,8 @@
"time_taken": "benötigte Zeit", "time_taken": "benötigte Zeit",
"player_name": "Spielername", "player_name": "Spielername",
"correct_answer_plural": "{{count}} richtige Antworten" "correct_answer_plural": "{{count}} richtige Antworten"
},
"navbar": {
"donate": "Spenden"
} }
} }
+67 -7
View File
@@ -48,7 +48,8 @@
"download_quizzes": "Download Quizzes", "download_quizzes": "Download Quizzes",
"download_quizzes_content": "Quizzes can be downloaded as one file and imported at any time. This also lets you move your quizzes to other ClassQuiz instances.", "download_quizzes_content": "Quizzes can be downloaded as one file and imported at any time. This also lets you move your quizzes to other ClassQuiz instances.",
"community_driven": "Community-driven", "community_driven": "Community-driven",
"community_driven_content": "ClassQuiz depends on its community for funding, testing ideas, feature requests, translations and more! You can also be a part of the ClassQuiz-community!" "community_driven_content": "ClassQuiz depends on its community for funding, testing ideas, feature requests, translations and more! You can also be a part of the ClassQuiz-community!",
"how_does_classquiz_work": "How does ClassQuiz even work?"
}, },
"overview_page": { "overview_page": {
"created_at": "Created at", "created_at": "Created at",
@@ -161,7 +162,13 @@
"results": "Results", "results": "Results",
"note": "Note", "note": "Note",
"player_plural": "Players", "player_plural": "Players",
"score": "Score" "score": "Score",
"slide": "Slide",
"name": "Name",
"point": "Point",
"point_plural": "Points",
"back": "Back",
"finish": "Finish"
}, },
"editor": { "editor": {
"time_in_seconds": "Time in seconds", "time_in_seconds": "Time in seconds",
@@ -170,7 +177,18 @@
"add_new_question": "Add new question", "add_new_question": "Add new question",
"delete_question": "Delete question", "delete_question": "Delete question",
"delete_answer": "Delete answer", "delete_answer": "Delete answer",
"not_all_links_imgur_links": "Not all links are Imgur-links!" "not_all_links_imgur_links": "Not all links are Imgur-links!",
"no_title": "No title...",
"empty": "Empty...",
"bg_image": "Background image",
"slide": {
"headline": "Headline",
"headline_description": "A bold text for headlines",
"text": "Text",
"text_description": "Smaller longer text",
"rectangle": "Rectangle",
"rectangle_description": "Just a rectangle"
}
}, },
"import_page": { "import_page": {
"need_help": "Need help?", "need_help": "Need help?",
@@ -194,7 +212,11 @@
"start_by_showing_first_question": "Start by showing the first question.", "start_by_showing_first_question": "Start by showing the first question.",
"no_answers": "No answers!", "no_answers": "No answers!",
"stop_time": "Stop time", "stop_time": "Stop time",
"save_results": "Save results" "save_results": "Save results",
"next_question": "Next Question ({{question}})",
"show_results": "Show results",
"stop_time_and_solutions": "Stop time and show solutions",
"enter_answer_into_field": "Enter your answer into the input field!"
}, },
"password_reset_page": { "password_reset_page": {
"reset_password": "Reset password" "reset_password": "Reset password"
@@ -207,7 +229,9 @@
"last_seen": "Last seen", "last_seen": "Last seen",
"check_location": "Check location", "check_location": "Check location",
"delete_this_session": "Delete this session", "delete_this_session": "Delete this session",
"this_session?": "This session?" "this_session?": "This session?",
"change_avatar": "Change avatar",
"security_settings": "Security-Settings"
}, },
"explore_page": { "explore_page": {
"made_by": "Made by", "made_by": "Made by",
@@ -222,7 +246,12 @@
"1st_place": "1st Place", "1st_place": "1st Place",
"2nd_place": "2nd Place", "2nd_place": "2nd Place",
"3rd place": "3rd Place", "3rd place": "3rd Place",
"with_out_of": "with {{correct_questions}} out of {{total_question_count}}" "with_out_of": "with {{correct_questions}} out of {{total_question_count}}",
"final_result_rank": "{{place}}: {{username}} with {{points}} points",
"your_score": "Your score: {{score}}",
"join_description": "Join at {{url}} and enter {{pin}}.",
"join_by_entering_code": "Join by entering the following code",
"points_added": "Points added"
}, },
"editor_page": { "editor_page": {
"add_an_answer": "Add an answer", "add_an_answer": "Add an answer",
@@ -257,7 +286,8 @@
"clothe_color": "Clothing color", "clothe_color": "Clothing color",
"clothe_graphic_type": "Graphic", "clothe_graphic_type": "Graphic",
"thats_you": "That's You!", "thats_you": "That's You!",
"start_over": "Start over" "start_over": "Start over",
"go_back": "Go back"
}, },
"results_page": { "results_page": {
"no_results_so_far": "No results saved so far...", "no_results_so_far": "No results saved so far...",
@@ -272,5 +302,35 @@
"correct_answer": "{{count}} correct answer", "correct_answer": "{{count}} correct answer",
"correct_answer_plural": "{{count}} correct answers", "correct_answer_plural": "{{count}} correct answers",
"time_taken": "Time taken" "time_taken": "Time taken"
},
"navbar": {
"donate": "Donate"
},
"security_settings": {
"backup_code": "Backup-Code",
"get_backup_code": "Get Backup-Code",
"activate_2fa": "Activate Two Factor Authentication",
"2fa_activated": "Two Factor authentication is activated",
"2fa_deactivated": "Two Factor authentication is deactivated",
"webauthn": "Webauthn",
"webauthn_available": "Webauthn is available",
"webauthn_unavailable": "Webauthn is not available",
"add_security_key": "Add Security-Key",
"totp": "Totp",
"totp_available": "Totp is available",
"totp_unavailable": "Totp is not available",
"disable_totp": "Disable Totp",
"enable_totp": "Enable Totp",
"backup_codes": {
"your_backup_code": "Your Backup-Code",
"save_somewhere_save": "Save this somewhere safe!",
"download_code": "Download code"
},
"totp_setup": {
"scan_to_set_up": "Scan this QR-code to set up the code",
"enter_as_secret_if_no_see_code": "Enter this as the secret if you can't scan the QR-code",
"totp_setup": "Totp-Setup",
"do_not_forget_backup_code": "Do not forget to save your recovery-code!"
}
} }
} }
+2 -2
View File
@@ -109,7 +109,7 @@
<ul id="menu-items" class="lg:flex w-full flex-col lg:flex-row" class:hidden={openMenu}> <ul id="menu-items" class="lg:flex w-full flex-col lg:flex-row" class:hidden={openMenu}>
<li class="py-2 lg:hidden"> <li class="py-2 lg:hidden">
<BrownButton href="https://mawoka.eu/donate" target="_blank" <BrownButton href="https://mawoka.eu/donate" target="_blank"
>Donate <span class="">❤️</span></BrownButton >{$t('navbar.donate')} <span class="">❤️</span></BrownButton
> >
</li> </li>
{#if $signedIn} {#if $signedIn}
@@ -183,7 +183,7 @@
> >
<div class="whitespace-nowrap hidden lg:block"> <div class="whitespace-nowrap hidden lg:block">
<BrownButton href="https://mawoka.eu/donate" target="_blank" <BrownButton href="https://mawoka.eu/donate" target="_blank"
>Donate <span class="">❤️</span></BrownButton >{$t('navbar.donate')} <span class="">❤️</span></BrownButton
> >
</div> </div>
{#if darkMode} {#if darkMode}
@@ -5,6 +5,9 @@
--> -->
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
export let data; export let data;
export let username; export let username;
@@ -53,8 +56,11 @@
style="font-size: {player_count_or_five - i / 2}rem" style="font-size: {player_count_or_five - i / 2}rem"
class="text-center" class="text-center"
> >
{i + 1} {$t('play_page.final_result_rank', {
: {player} with {data[player]} points place: i + 1,
username: player,
points: data[player]
})}
</p> </p>
{/if} {/if}
{/each} {/each}
@@ -62,7 +68,7 @@
{#if data[username]} {#if data[username]}
<div class="fixed bottom-0 left-0 flex justify-center w-full mb-6"> <div class="fixed bottom-0 left-0 flex justify-center w-full mb-6">
<div class="mx-auto p-2 border-[#B07156] border-4 rounded"> <div class="mx-auto p-2 border-[#B07156] border-4 rounded">
<p>Your score: <b>{data[username]}</b></p> <p>{$t('play_page.your_score', { score: data[username] })}</p>
</div> </div>
</div> </div>
{/if} {/if}
@@ -43,7 +43,10 @@
<div class="grid grid-cols-3 mt-12"> <div class="grid grid-cols-3 mt-12">
<div class="flex justify-center"> <div class="flex justify-center">
<p class="m-auto text-2xl"> <p class="m-auto text-2xl">
Join at <b>{window.location.host}/play</b> and enter <b>{game_pin}</b>. {$t('play_page.join_description', {
url: `${window.location.host}/play`,
pin: game_pin
})}
</p> </p>
</div> </div>
<img <img
@@ -54,7 +57,7 @@
{#if cqc_code} {#if cqc_code}
<div class="m-auto"> <div class="m-auto">
<div class="flex-col flex justify-center"> <div class="flex-col flex justify-center">
<p class="mx-auto">Join by entering the following code</p> <p class="mx-auto">{$t('play_page.join_by_entering_code')}</p>
<div class="flex flex-row gap-2 mx-auto"> <div class="flex flex-row gap-2 mx-auto">
{#each cqc_code as c} {#each cqc_code as c}
<div class="flex flex-col"> <div class="flex flex-col">
+7 -3
View File
@@ -7,6 +7,8 @@
import { flip } from 'svelte/animate'; import { flip } from 'svelte/animate';
import { fly } from 'svelte/transition'; import { fly } from 'svelte/transition';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
export let data; export let data;
@@ -91,11 +93,13 @@
<div> <div>
<table class="table-auto text-xl"> <table class="table-auto text-xl">
<tr> <tr>
<th class="p-2 border-r border-r-black border-b-2 border-b-black">Name</th> <th class="p-2 border-r border-r-black border-b-2 border-b-black"
<th class="p-2 border-b-2 border-b-black">Points</th> >{$t('words.name')}</th
>
<th class="p-2 border-b-2 border-b-black">{$t('words.point', { count: 2 })}</th>
{#if show_new_score_clicked} {#if show_new_score_clicked}
<th in:fly={{ x: 300 }} class="p-2 border-b-2 border-b-black" <th in:fly={{ x: 300 }} class="p-2 border-b-2 border-b-black"
>Points added >{$t('play_page.points_added')}
</th> </th>
{/if} {/if}
</tr> </tr>
+1 -1
View File
@@ -208,7 +208,7 @@
</div> </div>
</section> </section>
<section> <section>
<h2 class="text-center text-5xl mb-6">How does ClassQuiz even work?</h2> <h2 class="text-center text-5xl mb-6">{$t('index_page.how_does_classquiz_work')}</h2>
<div class="flex justify-center w-full"> <div class="flex justify-center w-full">
<h3 class="text-center text-3xl rounded-t-lg bg-opacity-40 bg-white py-2 px-6"> <h3 class="text-center text-3xl rounded-t-lg bg-opacity-40 bg-white py-2 px-6">
@@ -170,7 +170,9 @@
alt="Profile image of {user.username}" alt="Profile image of {user.username}"
/> />
<div class="m-2 flex justify-center"> <div class="m-2 flex justify-center">
<BrownButton href="/account/settings/avatar">Change avatar</BrownButton> <BrownButton href="/account/settings/avatar"
>{$t('settings_page.change_avatar')}</BrownButton
>
</div> </div>
</div> </div>
<div class="grid grid-rows-2 col-start-2 col-end-7"> <div class="grid grid-rows-2 col-start-2 col-end-7">
@@ -184,7 +186,7 @@
<div class="p-4 flex justify-center"> <div class="p-4 flex justify-center">
<div class="m-auto"> <div class="m-auto">
<BrownButton href="/account/settings/security" <BrownButton href="/account/settings/security"
>Security-Settings >{$t('settings_page.security_settings')}
</BrownButton> </BrownButton>
</div> </div>
</div> </div>
@@ -219,7 +221,7 @@
</div> </div>
</form> </form>
<div> <div>
<button on:click={add_api_key}>Add API-Key</button> <button on:click={add_api_key}>{$t('settings_page.security_settings')}</button>
{#await api_keys} {#await api_keys}
<Spinner /> <Spinner />
{:then keys} {:then keys}
@@ -231,7 +233,7 @@
delete_api_key(key.key); delete_api_key(key.key);
}} }}
class="admin-button" class="admin-button"
>Delete >{$t('words.delete')}
</button> </button>
</div> </div>
{/each} {/each}
@@ -94,7 +94,7 @@
on:click={() => { on:click={() => {
index = index - 1; index = index - 1;
}} }}
disabled={index < 1}>Back</BrownButton disabled={index < 1}>{$t('words.back')}</BrownButton
> >
</div> </div>
<div class="mx-auto"> <div class="mx-auto">
@@ -103,7 +103,7 @@
</h2> </h2>
</div> </div>
<div class="ml-auto"> <div class="ml-auto">
<BrownButton disabled={index < 11}>Finish</BrownButton> <BrownButton disabled={index < 11}>{$t('words.finish')}</BrownButton>
</div> </div>
</div> </div>
<div class="grid grid-cols-4"> <div class="grid grid-cols-4">
@@ -167,11 +167,11 @@
<Spinner my_20={false} /> <Spinner my_20={false} />
{/if} {/if}
</BrownButton> </BrownButton>
<BrownButton href="/account/settings">Go back</BrownButton> <BrownButton href="/account/settings">{$t('avatar_settings.go_back')}</BrownButton>
<BrownButton <BrownButton
on:click={() => { on:click={() => {
finished = false; finished = false;
}}>Close</BrownButton }}>{$t('words.close')}</BrownButton
> >
</div> </div>
</div> </div>
@@ -10,6 +10,9 @@
import TotpSetup from './totp_setup.svelte'; import TotpSetup from './totp_setup.svelte';
import BackupCodes from './backup_codes.svelte'; import BackupCodes from './backup_codes.svelte';
import BrownButton from '$lib/components/buttons/brown.svelte'; import BrownButton from '$lib/components/buttons/brown.svelte';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
let user_data: object | undefined; let user_data: object | undefined;
let security_keys: Array<{ id: number }> | undefined; let security_keys: Array<{ id: number }> | undefined;
@@ -101,15 +104,17 @@
<div class="grid grid-rows-2 h-screen"> <div class="grid grid-rows-2 h-screen">
<div class="grid grid-cols-2 h-full border-b-2 border-black"> <div class="grid grid-cols-2 h-full border-b-2 border-black">
<div class="h-full w-full border-r-2 border-black"> <div class="h-full w-full border-r-2 border-black">
<h2 class="text-center text-2xl">Backup-Code</h2> <h2 class="text-center text-2xl">{$t('security_settings.backup_code')}</h2>
<div class="flex h-full w-full justify-center"> <div class="flex h-full w-full justify-center">
<div class="m-auto"> <div class="m-auto">
<BrownButton on:click={get_backup_code}>Get Backup-Codes</BrownButton> <BrownButton on:click={get_backup_code}
>{$t('security_settings.get_backup_code')}</BrownButton
>
</div> </div>
</div> </div>
</div> </div>
<div class="h-full w-full"> <div class="h-full w-full">
<h2 class="text-center text-2xl">Activate 2 Factor</h2> <h2 class="text-center text-2xl">{$t('security_settings.activate_2fa')}</h2>
<div class="flex h-full w-full justify-center flex-col"> <div class="flex h-full w-full justify-center flex-col">
<div class="m-auto"> <div class="m-auto">
{#if user_data.require_password} {#if user_data.require_password}
@@ -130,7 +135,7 @@
/> />
</button> </button>
<span class="text-sm font-medium text-gray-700 dark:text-white" <span class="text-sm font-medium text-gray-700 dark:text-white"
>Two Factor authentication is activated</span >{$t('security_settings.2fa_activated')}</span
> >
</div> </div>
{:else} {:else}
@@ -151,7 +156,7 @@
/> />
</button> </button>
<span class="text-sm font-medium text-gray-700 dark:text-white" <span class="text-sm font-medium text-gray-700 dark:text-white"
>Two Factor authentication is deactivated</span >{$t('security_settings.2fa_deactivated')}</span
> >
</div> </div>
{/if} {/if}
@@ -161,17 +166,19 @@
</div> </div>
<div class="grid grid-cols-2 h-full"> <div class="grid grid-cols-2 h-full">
<div class="h-full w-full flex flex-col border-r-2 border-black"> <div class="h-full w-full flex flex-col border-r-2 border-black">
<h2 class="text-center text-2xl">Webauthn</h2> <h2 class="text-center text-2xl">{$t('security_settings.webauthn')}</h2>
<div class="flex justify-center"> <div class="flex justify-center">
{#if security_keys.length > 0} {#if security_keys.length > 0}
<p>Webauthn is available</p> <p>{$t('security_settings.webauthn_available')}</p>
{:else} {:else}
<p>Webauthn is not available</p> <p>{$t('security_settings.webauthn_unavailable')}</p>
{/if} {/if}
</div> </div>
<div class="flex justify-center"> <div class="flex justify-center">
<div class="m-auto"> <div class="m-auto">
<BrownButton on:click={add_security_key}>Add Security-Key</BrownButton> <BrownButton on:click={add_security_key}
>{$t('security_settings.add_security_key')}</BrownButton
>
</div> </div>
</div> </div>
<div class="flex justify-center"> <div class="flex justify-center">
@@ -190,21 +197,25 @@
</div> </div>
</div> </div>
<div class="h-full w-full flex flex-col"> <div class="h-full w-full flex flex-col">
<h2 class="text-center text-2xl">Totp</h2> <h2 class="text-center text-2xl">{$t('security_settings.totp')}</h2>
<div class="flex justify-center"> <div class="flex justify-center">
{#if totp_activated} {#if totp_activated}
<p>Totp is available</p> <p>{$t('security_settings.totp_available')}</p>
{:else} {:else}
<p>Totp is not available</p> <p>{$t('security_settings.totp_unavailable')}</p>
{/if} {/if}
</div> </div>
<div class="flex justify-center"> <div class="flex justify-center">
<div class="m-auto"> <div class="m-auto">
{#if totp_activated} {#if totp_activated}
<BrownButton on:click={disable_totp}>Disable Totp</BrownButton> <BrownButton on:click={disable_totp}
>{$t('security_settings.disable_totp')}</BrownButton
>
{:else} {:else}
<BrownButton on:click={enable_totp}>Enable Totp</BrownButton> <BrownButton on:click={enable_totp}
>{$t('security_settings.enable_totp')}</BrownButton
>
{/if} {/if}
</div> </div>
</div> </div>
@@ -4,6 +4,9 @@
- file, You can obtain one at https://mozilla.org/MPL/2.0/. - file, You can obtain one at https://mozilla.org/MPL/2.0/.
--> -->
<script lang="ts"> <script lang="ts">
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
export let backup_code; export let backup_code;
let already_downloaded = false; let already_downloaded = false;
@@ -30,12 +33,12 @@
on:click={() => { on:click={() => {
backup_code = undefined; backup_code = undefined;
}} }}
>Close >{$t('words.close')}
</button> </button>
<div <div
class="bg-white dark:bg-gray-700 rounded-b-lg rounded-tr-lg w-full h-full flex flex-col" class="bg-white dark:bg-gray-700 rounded-b-lg rounded-tr-lg w-full h-full flex flex-col"
> >
<h2 class="text-3xl m-auto">Your Backup-Code</h2> <h2 class="text-3xl m-auto">{$t('security_settings.backup_codes.your_backup_code')}</h2>
<p <p
class="select-all font-mono text-xl m-auto" class="select-all font-mono text-xl m-auto"
on:click={() => { on:click={() => {
@@ -44,13 +47,14 @@
> >
{backup_code} {backup_code}
</p> </p>
<p class="m-auto">Save this somewhere safe!</p> <p class="m-auto">{$t('security_settings.backup_codes.save_somewhere_save')}</p>
<button <button
on:click={() => { on:click={() => {
download_code(true); download_code(true);
}} }}
class="m-auto p-2 bg-[#B07156] rounded-lg">Download code</button class="m-auto p-2 bg-[#B07156] rounded-lg"
> >{$t('security_settings.backup_codes.download_code')}
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -6,6 +6,9 @@
<script lang="ts"> <script lang="ts">
import QRCode from 'qrcode'; import QRCode from 'qrcode';
import Spinner from '$lib/Spinner.svelte'; import Spinner from '$lib/Spinner.svelte';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
export let totp_data: { url: string; secret: string } | undefined; export let totp_data: { url: string; secret: string } | undefined;
@@ -21,23 +24,25 @@
on:click={() => { on:click={() => {
totp_data = undefined; totp_data = undefined;
}} }}
>Close >{$t('words.close')}
</button> </button>
<div class="bg-white dark:bg-gray-700 rounded-b-lg rounded-tr-lg w-full h-full"> <div class="bg-white dark:bg-gray-700 rounded-b-lg rounded-tr-lg w-full h-full">
<div class="grid grid-cols-3 w-full h-full"> <div class="grid grid-cols-3 w-full h-full">
<div class="flex flex-col justify-center w-full h-5/6"> <div class="flex flex-col justify-center w-full h-5/6">
<span class="m-auto" /> <span class="m-auto" />
<div class="h-5/6 flex"> <div class="h-5/6 flex">
<p class="my-auto ml-auto">Scan this to set up the code</p> <p class="my-auto ml-auto">
{$t('security_settings.totp_setup.scan_to_set_up')}
</p>
</div> </div>
<div class="flex"> <div class="flex">
<p class="my-auto ml-auto"> <p class="my-auto ml-auto">
Enter this as the secret if you can't scan the code {$t('security_settings.totp_setup.enter_as_secret_if_no_see_code')}
</p> </p>
</div> </div>
</div> </div>
<div class="flex flex-col justify-start w-full h-5/6"> <div class="flex flex-col justify-start w-full h-5/6">
<h2 class="text-2xl m-auto">Totp-Setup</h2> <h2 class="text-2xl m-auto">{$t('security_settings.totp_setup.totp_setup')}</h2>
{#await get_image_url()} {#await get_image_url()}
<Spinner my_20={false} /> <Spinner my_20={false} />
{:then data} {:then data}
@@ -52,7 +57,9 @@
<p class="m-auto select-all font-mono">{totp_data.secret}</p> <p class="m-auto select-all font-mono">{totp_data.secret}</p>
</div> </div>
<div class="flex justify-center h-5/6 w-full"> <div class="flex justify-center h-5/6 w-full">
<p class="m-auto text-3xl p-4">Do not forget to save your recovery-code!</p> <p class="m-auto text-3xl p-4">
{$t('security_settings.totp_setup.do_not_forget_backup_code')}
</p>
</div> </div>
</div> </div>
</div> </div>
@@ -5,6 +5,9 @@
--> -->
<script lang="ts"> <script lang="ts">
import type { Question } from '$lib/quiz_types'; import type { Question } from '$lib/quiz_types';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
export let questions: Question[]; export let questions: Question[];
export let answers: { export let answers: {
@@ -41,10 +44,12 @@
<div class="w-full"> <div class="w-full">
<div class="flex justify-center w-full"> <div class="flex justify-center w-full">
<p class="text-3xl w-5/6 text-center"> <p class="text-3xl w-5/6 text-center">
The quiz with the title '<strong>{title}</strong>' was played on {$t('results_page.general_overview.sentence', {
<strong>{new Date(timestamp).toLocaleString()}</strong> title,
with <strong>{usernames.length}</strong> players. The players achieved an average score date: new Date(timestamp).toLocaleString(),
of <strong>{get_average_final_score()}</strong>. player_count: usernames.length,
average_score: get_average_final_score()
})}
</p> </p>
</div> </div>
</div> </div>
@@ -25,7 +25,7 @@
<th class="border-r dark:border-gray-500 p-1 mx-auto border-gray-300" <th class="border-r dark:border-gray-500 p-1 mx-auto border-gray-300"
>{$t('result_page.player_name')} >{$t('result_page.player_name')}
</th> </th>
<th class="p-1 mx-auto">Player Score</th> <th class="p-1 mx-auto">{$t('result_page.player_score')}</th>
{#if Object.keys(custom_field).length !== 0} {#if Object.keys(custom_field).length !== 0}
<th class="border-l dark:border-gray-500 p-1 mx-auto border-gray-300" <th class="border-l dark:border-gray-500 p-1 mx-auto border-gray-300"
>{$t('result_page.custom_field')} >{$t('result_page.custom_field')}
@@ -40,7 +40,7 @@
<div class="grid grid-cols-6 h-full"> <div class="grid grid-cols-6 h-full">
<div class="pl-2"> <div class="pl-2">
{#if data.user.username === undefined} {#if data.user.username === undefined}
<h2 class="text-4xl text-center p-4">User not found</h2> <h2 class="text-4xl text-center p-4">{$t('public_user_page.user_not_found')}</h2>
{:else} {:else}
<div class="flex justify-center"> <div class="flex justify-center">
<img src={`/api/v1/users/avatar/${data.user.id}`} alt="profile" /> <img src={`/api/v1/users/avatar/${data.user.id}`} alt="profile" />
@@ -49,7 +49,9 @@
@{data.user.username} @{data.user.username}
</h2> </h2>
<p class="italic text-center"> <p class="italic text-center">
Joined on {new Date(data.user.created_at).toLocaleDateString()} {$t('public_user_page.joined_on', {
date: new Date(data.user.created_at).toLocaleDateString()
})}
</p> </p>
{/if} {/if}
</div> </div>
@@ -57,7 +59,7 @@
class="col-start-2 col-end-7 border-l border-black h-full p-4 overflow-y-scroll flex flex-col gap-4" class="col-start-2 col-end-7 border-l border-black h-full p-4 overflow-y-scroll flex flex-col gap-4"
> >
{#if data.quizzes.length === 0} {#if data.quizzes.length === 0}
<p class="text-center text-4xl">No original quizzes found...</p> <p class="text-center text-4xl">{$t('public_user_page.no_original_quizzes')}</p>
{:else} {:else}
{#each data.quizzes as quiz} {#each data.quizzes as quiz}
<div <div
@@ -71,7 +71,8 @@
<p>{@html quiz.description}</p> <p>{@html quiz.description}</p>
</div> </div>
<p class="text-center"> <p class="text-center">
Made by <a href="/user/{quiz.user_id.id}" class="underline">@{quiz.user_id.username}</a> {$t('view_quiz_page.made_by')}
<a href="/user/{quiz.user_id.id}" class="underline">@{quiz.user_id.username}</a>
</p> </p>
{#if quiz.cover_image} {#if quiz.cover_image}
<div class="flex justify-center align-middle items-center"> <div class="flex justify-center align-middle items-center">
@@ -95,7 +96,7 @@
href="https://create.kahoot.it/details/{quiz.kahoot_id}" href="https://create.kahoot.it/details/{quiz.kahoot_id}"
target="_blank" target="_blank"
> >
View on <i>Kahoot!</i> {$t('view_quiz_page.view_on_kahoot')}
</GrayButton> </GrayButton>
</div> </div>
{/if} {/if}