🌐 Added i18next

This commit is contained in:
Mawoka
2022-03-25 18:23:41 +01:00
parent 326780c8d7
commit 8f8e152c32
16 changed files with 4457 additions and 2495 deletions
+2
View File
@@ -4,6 +4,8 @@
import { navbarVisible } from '$lib/stores.ts';
import * as Sentry from '@sentry/browser';
import { BrowserTracing } from '@sentry/tracing';
import { initLocalizationContext } from '$lib/i18n';
initLocalizationContext();
if (import.meta.env.VITE_SENTRY !== null) {
Sentry.init({
+22 -20
View File
@@ -13,6 +13,9 @@
<script lang="ts">
import { navbarVisible } from '$lib/stores';
import { browser } from '$app/env';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
navbarVisible.set(true);
@@ -84,11 +87,11 @@
</h2>
<h3 class="mt-1 text-xl font-medium text-center text-gray-600 dark:text-gray-200">
Welcome Back
{$t('login_page.welcome_back')}
</h3>
<p class="mt-1 text-center text-gray-500 dark:text-gray-400">
Login or create account
{$t('login_page.login_or_create_account')}
</p>
<form on:submit|preventDefault={login}>
@@ -101,13 +104,13 @@
name="email"
type="email"
class="w-full peer bg-transparent h-10 rounded-lg text-gray-700 dark:text-white placeholder-transparent ring-2 px-2 ring-gray-500 focus:ring-sky-600 focus:outline-none focus:border-rose-600"
placeholder="Email"
placeholder={$t('register_page.email')}
/>
<label
for="email"
class="absolute cursor-text left-0 -top-3 text-sm text-gray-700 dark:text-white bg-inherit mx-1 px-1 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-500 peer-placeholder-shown:top-2 peer-focus:-top-3 peer-focus:text-sky-600 peer-focus:text-sm transition-all"
>
Email
{$t('register_page.email')}
</label>
</div>
</div>
@@ -119,13 +122,13 @@
type="password"
bind:value={loginData.password}
class="w-full peer bg-transparent h-10 rounded-lg text-gray-700 dark:text-white placeholder-transparent ring-2 px-2 ring-gray-500 focus:ring-sky-600 focus:outline-none focus:border-rose-600"
placeholder="Password"
placeholder={$t('register_page.password')}
/>
<label
for="password"
class="absolute cursor-text left-0 -top-3 text-sm text-gray-500 bg-inherit mx-1 px-1 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-500 peer-placeholder-shown:top-2 peer-focus:-top-3 peer-focus:text-sky-600 peer-focus:text-sm transition-all"
>
Password
{$t('register_page.password')}
</label>
</div>
</div>
@@ -134,7 +137,7 @@
<a
href="/account/reset-password"
class="text-sm text-gray-600 dark:text-gray-200 hover:text-gray-500"
>Forgot Password?</a
>{$t('register_page.forgot_password?')}</a
>
<button
@@ -157,7 +160,7 @@
/>
</svg>
{:else}
Login
{$t('register_page.login')}
{/if}
</button>
</div>
@@ -169,13 +172,13 @@
class="flex items-center justify-center py-4 text-center bg-gray-50 dark:bg-gray-700"
>
<span class="text-sm text-gray-600 dark:text-gray-200"
>Don't have an account?
>{$t('login_page.already_have_account')}
</span>
<a
href="/account/register"
class="mx-2 text-sm font-bold text-blue-500 dark:text-blue-400 hover:underline"
>Register</a
>{$t('register_page.register')}</a
>
</div>
</div>
@@ -267,13 +270,13 @@
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
{#if responseData.data === 'magic'}
Login Succeded! Check your mailbox!
{$t('login_page.modal.success.success_check_mail')}
{:else if responseData.data === 'password'}
Login Succeded!
{$t('login_page.modal.success.success')}
{:else if responseData.data === '404'}
Wrong email or password!
{$t('login_page.modal.error.wrong_creds')}
{:else if responseData.data === 'error'}
unexpected error!
{$t('login_page.modal.error.unexpected')}
{:else}
You stupid Mawoka!
{/if}
@@ -281,14 +284,13 @@
<div class="mt-2">
<p class="text-sm text-gray-500">
{#if responseData.data === 'magic'}
Please check your mailbox, since you should have received a
Mail, with a link you can click to login.
{$t('login_page.modal.success.description.success_check_mail')}
{:else if responseData.data === 'password'}
You've successfully logged in!
{$t('login_page.modal.success.description.success')}
{:else if responseData.data === '404'}
Please make sure that your password and your email are correct!
{$t('login_page.modal.error.description.wrong_creds')}
{:else if responseData.data === 'error'}
There was the good old unexpected error!
{$t('login_page.modal.error.description.unexpected')}
{:else}
You stupid Mawoka!
{/if}
@@ -307,7 +309,7 @@
}
}}
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
>Close
>{$t('create_page.close')}
</button>
</div>
</div>
+21 -15
View File
@@ -12,9 +12,13 @@
<script lang="ts">
import { createForm } from 'felte';
// import reporter from '@felte/reporter-tippy';
import { getLocalization } from '$lib/i18n';
import { validateSchema } from '@felte/validator-yup';
import { navbarVisible } from '$lib/stores';
const { t } = getLocalization();
// import reporter from '@felte/reporter-tippy';
navbarVisible.set(true);
import * as yup from 'yup';
@@ -83,10 +87,12 @@
</h2>
<h3 class="mt-1 text-xl font-medium text-center text-gray-600 dark:text-gray-200">
Nice to meet you!
{$t('register_page.greeting')}
</h3>
<p class="mt-1 text-center text-gray-500 dark:text-gray-400">Create account</p>
<p class="mt-1 text-center text-gray-500 dark:text-gray-400">
{$t('register_page.create_account')}
</p>
<form use:form>
<div class="w-full mt-4">
@@ -97,7 +103,7 @@
name="email"
type="email"
class="w-full peer bg-transparent h-10 rounded-lg text-gray-700 dark:text-white placeholder-transparent ring-2 px-2 ring-gray-500 focus:ring-sky-600 focus:outline-none focus:border-rose-600"
placeholder="Email"
placeholder={$t('register_page.email')}
class:ring-red-700={$errors.email !== null}
class:ring-green-600={$touched.email === true &&
$errors.email === null}
@@ -106,7 +112,7 @@
for="email"
class="absolute cursor-text left-0 -top-3 text-sm text-gray-700 dark:text-white bg-inherit mx-1 px-1 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-500 peer-placeholder-shown:top-2 peer-focus:-top-3 peer-focus:text-sky-600 peer-focus:text-sm transition-all"
>
Email
{$t('register_page.email')}
</label>
</div>
</div>
@@ -117,7 +123,7 @@
name="username"
type="text"
class="w-full peer bg-transparent h-10 rounded-lg text-gray-700 dark:text-white placeholder-transparent ring-2 px-2 ring-gray-500 focus:ring-sky-600 focus:outline-none focus:border-rose-600"
placeholder="Username"
placeholder={$t('register_page.username')}
class:ring-red-700={$errors.username !== null}
class:ring-green-600={$touched.username === true &&
$errors.username === null}
@@ -126,7 +132,7 @@
for="username"
class="absolute cursor-text left-0 -top-3 text-sm text-gray-700 dark:text-white bg-inherit mx-1 px-1 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-500 peer-placeholder-shown:top-2 peer-focus:-top-3 peer-focus:text-sky-600 peer-focus:text-sm transition-all"
>
Username
{$t('register_page.username')}
</label>
</div>
</div>
@@ -140,13 +146,13 @@
class:ring-green-600={$touched.password1 === true &&
$errors.password1 === null}
class="w-full peer bg-transparent h-10 rounded-lg text-gray-700 dark:text-white placeholder-transparent ring-2 px-2 ring-gray-500 focus:ring-sky-600 focus:outline-none focus:border-rose-600"
placeholder="Password"
placeholder={$t('register_page.password')}
/>
<label
for="password1"
class="absolute cursor-text left-0 -top-3 text-sm text-gray-500 bg-inherit mx-1 px-1 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-500 peer-placeholder-shown:top-2 peer-focus:-top-3 peer-focus:text-sky-600 peer-focus:text-sm transition-all"
>
Password
{$t('register_page.password')}
</label>
</div>
</div>
@@ -157,7 +163,7 @@
name="password2"
type="password"
class="w-full peer bg-transparent h-10 rounded-lg text-gray-700 dark:text-white placeholder-transparent ring-2 px-2 ring-gray-500 focus:ring-sky-600 focus:outline-none focus:border-rose-600"
placeholder="Password"
placeholder={$t('register_page.password')}
class:ring-red-700={$errors.password2 !== null}
class:ring-green-600={$touched.password2 === true &&
$errors.password2 === null}
@@ -166,7 +172,7 @@
for="password2"
class="absolute cursor-text left-0 -top-3 text-sm text-gray-500 bg-inherit mx-1 px-1 peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-500 peer-placeholder-shown:top-2 peer-focus:-top-3 peer-focus:text-sky-600 peer-focus:text-sm transition-all"
>
Password
{$t('register_page.password')}
</label>
</div>
</div>
@@ -175,7 +181,7 @@
<a
href="/account/reset-password"
class="text-sm text-gray-600 dark:text-gray-200 hover:text-gray-500"
>Forgot Password?</a
>{$t('register_page.forgot_password?')}</a
>
<button
@@ -197,7 +203,7 @@
/>
</svg>
{:else}
Register
{$t('register_page.register')}
{/if}
</button>
</div>
@@ -209,13 +215,13 @@
class="flex items-center justify-center py-4 text-center bg-gray-50 dark:bg-gray-700"
>
<span class="text-sm text-gray-600 dark:text-gray-200"
>Already have an account?
>{$t('register_page.already_have_account?')}
</span>
<a
href="/account/login"
class="mx-2 text-sm font-bold text-blue-500 dark:text-blue-400 hover:underline"
>Login</a
>{$t('register_page.login')}</a
>
</div>
</div>
+6 -3
View File
@@ -25,6 +25,9 @@
<script lang="ts">
import { onMount } from 'svelte';
import Editor from '$lib/editor.svelte';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
interface Data {
public: boolean;
@@ -134,10 +137,10 @@
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
Successfully created quiz!
{$t('create_page.success.title')}
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">Created the quiz successfully!</p>
<p class="text-sm text-gray-500">{$t('create_page.success.body')}</p>
</div>
</div>
</div>
@@ -149,7 +152,7 @@
window.location.href = '/overview';
}}
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
>Close
>{$t('create_page.close')}
</button>
</div>
</div>
@@ -15,8 +15,8 @@
href="https://create.kahoot.it/discover"
rel="nofollow"
target="_blank">create.kahoot.it/discover</a
>, find the quiz you want to import, click on it and copy the URL into your clipboard.
Then go to
>, find the quiz you want to import, click on it and copy the URL into your clipboard. Then
go to
<a href="https://classquiz.mawoka.eu/import" rel="nofollow" target="_blank"
>classquiz.mawoka.eu/import</a
> and paste the url into the text field.
+9 -4
View File
@@ -16,6 +16,9 @@
<script lang="ts">
import Editor from '$lib/editor.svelte';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
let responseData = {
open: false,
@@ -88,7 +91,7 @@
{:then lol}
{#if data !== undefined}
<form on:submit|preventDefault={submit} class="grid grid-cols-1 gap-2">
<Editor bind:data submit_button_text={'Save'} />
<Editor bind:data submit_button_text={$t('edit_page.save')} />
</form>
{/if}
{:catch err}
@@ -141,10 +144,12 @@
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
Successfully updated quiz!
{$t('edit_page.success_update_title')}
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">Updated the quiz successfully!</p>
<p class="text-sm text-gray-500">
{$t('edit_page.success_update_body')}
</p>
</div>
</div>
</div>
@@ -156,7 +161,7 @@
window.location.href = '/overview';
}}
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
>Close
>{$t('edit_page.close')}
</button>
</div>
</div>
+11 -11
View File
@@ -1,5 +1,8 @@
<script lang="ts">
import { navbarVisible } from '$lib/stores';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
navbarVisible.set(true);
@@ -15,26 +18,23 @@
</script>
<svelte:head>
<title>ClassQuiz - Home</title>
<meta
name="description"
content="ClassQuiz is a quiz app like KAHOOT! for students, which is open source and free to use."
/>
<title>ClassQuiz - {$t('index_page.meta.title')}</title>
<meta name="description" content={$t('index_page.meta.description')} />
</svelte:head>
<section>
<div class="pt-12 text-center">
<h1 class="sm:text-8xl text-6xl mt-6 marck-script">ClassQuiz</h1>
<p class="text-xl mt-4">The open-source quiz-platform!</p>
<p class="text-xl mt-4">{$t('index_page.slogan')}</p>
</div>
</section>
<section id="stats">
<div class="text-center pt-48 snap-y">
<h1 class="sm:text-6xl text-4xl">Stats</h1>
<h1 class="sm:text-6xl text-4xl">{$t('index_page.stats')}</h1>
<p class="text-xl pt-4">
{#await getStats() then stats}
There are already <span class="underline">{stats.user_count}</span> users and
There are already <span class="underline">{stats.user_count}</span> users and <!-- TODO: Add translation -->
<span class="underline">{stats.quiz_count}</span> quizzes on ClassQuiz.
{/await}
</p>
@@ -42,11 +42,11 @@
</section>
<section id="features">
<div class="text-center pt-24 snap-y">
<h1 class="sm:text-6xl text-4xl">Features</h1>
<h1 class="sm:text-6xl text-4xl">{$t('index_page.features')}</h1>
<p class="text-xl pt-4">
ClassQuiz is a quiz-platform that allows you to create and manage quizzes.
{$t('index_page.features_description.1')}
<br />
The main feature is a KAHOOT!-import function that allows you to import quizzes from KAHOOT!-quizzes.
{$t('index_page.features_description.2')}
</p>
</div>
</section>
+14 -12
View File
@@ -17,6 +17,8 @@
<script lang="ts">
import type { QuizData } from '../app';
import { DateTime } from 'luxon';
import { getLocalization } from '$lib/i18n';
const { t } = getLocalization();
const getData = async (): Promise<Array<QuizData>> => {
const res = await fetch('/api/v1/quiz/list');
@@ -76,17 +78,17 @@
<a
href="/create"
class="px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded text-center hover:bg-gray-600 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
>Create
>{$t('overview_page.create')}
</a>
<a
href="/import"
class="px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded text-center hover:bg-gray-600 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
>Import
>{$t('overview_page.import')}
</a>
<a
href="/api/v1/users/logout"
class="px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded text-center hover:bg-gray-600 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
>Logout
>{$t('overview_page.logout')}
</a>
</div>
<div class="overflow-x-auto sm:-mx-8 lg:-mx-8">
@@ -99,43 +101,43 @@
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
Title
{$t('overview_page.title')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
Created at
{$t('overview_page.created_at')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
Question count
{$t('overview_page.question_count')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
Play
{$t('overview_page.play')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
Edit
{$t('overview_page.edit')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
Delete
{$t('overview_page.delete')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
Public
{$t('overview_page.public')}
</th>
</tr>
</thead>
@@ -167,7 +169,7 @@
}}
class="border border-green-600"
>
Start
{$t('overview_page.start')}
</button>
</td>
<td
@@ -184,7 +186,7 @@
}}
class="border border-green-600"
>
Delete
{$t('overview_page.delete')}
</button>
</td>
<td