🌐 Added i18next
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
module.exports = {
|
||||||
|
options: {
|
||||||
|
debug: true,
|
||||||
|
// read strings from functions: IllegalMoveError('KEY') or t('KEY')
|
||||||
|
func: {
|
||||||
|
list: ['IllegalMoveError', 't'],
|
||||||
|
extensions: ['.js', '.svelte']
|
||||||
|
},
|
||||||
|
|
||||||
|
trans: false,
|
||||||
|
|
||||||
|
// Create and update files `en.json`, `fr.json`, `es.json`
|
||||||
|
lngs: ['en', 'de'],
|
||||||
|
|
||||||
|
ns: [
|
||||||
|
// The namespace I use
|
||||||
|
'translation'
|
||||||
|
],
|
||||||
|
|
||||||
|
defaultLng: 'en',
|
||||||
|
defaultNs: 'translation',
|
||||||
|
|
||||||
|
// Put a blank string as initial translation
|
||||||
|
// (useful for Weblate be marked as 'not yet translated', see later)
|
||||||
|
defaultValue: (lng, ns, key) => '',
|
||||||
|
|
||||||
|
// Location of translation files
|
||||||
|
resource: {
|
||||||
|
loadPath: 'src/lib/i18n/locales/{{lng}}.json',
|
||||||
|
savePath: 'src/lib/i18n/locales/{{lng}}.json',
|
||||||
|
jsonIndent: 4
|
||||||
|
},
|
||||||
|
|
||||||
|
nsSeparator: ':',
|
||||||
|
keySeparator: '.'
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -10,7 +10,8 @@
|
|||||||
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
"lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
||||||
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. .",
|
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. .",
|
||||||
"run:prod": "node index.js"
|
"run:prod": "node index.js",
|
||||||
|
"translations-scan": "i18next-scanner --config i18next-scanner.config.engine.cjs src/routes/*/*.svelte"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@felte/validator-yup": "^1.0.1",
|
"@felte/validator-yup": "^1.0.1",
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
"eslint-plugin-svelte3": "^3.2.1",
|
"eslint-plugin-svelte3": "^3.2.1",
|
||||||
"felte": "^1.0.1",
|
"felte": "^1.0.1",
|
||||||
"highlight.js": "^11.4.0",
|
"highlight.js": "^11.4.0",
|
||||||
|
"i18next-scanner": "^3.1.0",
|
||||||
"ioredis": "^4.28.5",
|
"ioredis": "^4.28.5",
|
||||||
"js-cookie": "^3.0.1",
|
"js-cookie": "^3.0.1",
|
||||||
"luxon": "^2.3.1",
|
"luxon": "^2.3.1",
|
||||||
@@ -51,5 +53,8 @@
|
|||||||
"typescript": "~4.5.4",
|
"typescript": "~4.5.4",
|
||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"i18next": "^21.6.14"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1973
-298
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
|||||||
|
import i18next from 'i18next';
|
||||||
|
import translations from './translations';
|
||||||
|
import en from './locales/en.json';
|
||||||
|
|
||||||
|
import type { i18n, Resource } from 'i18next';
|
||||||
|
|
||||||
|
const INITIAL_LANGUAGE = 'en';
|
||||||
|
|
||||||
|
export class I18nService {
|
||||||
|
// expose i18next
|
||||||
|
i18n: i18n;
|
||||||
|
constructor() {
|
||||||
|
this.i18n = i18next;
|
||||||
|
this.initialize();
|
||||||
|
this.changeLanguage(INITIAL_LANGUAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our translation function
|
||||||
|
t(key: string, replacements?: Record<string, unknown>): string {
|
||||||
|
return this.i18n.t(key, replacements);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializing i18n
|
||||||
|
initialize() {
|
||||||
|
this.i18n.init({
|
||||||
|
lng: INITIAL_LANGUAGE,
|
||||||
|
fallbackLng: 'en',
|
||||||
|
debug: false,
|
||||||
|
defaultNS: 'translation',
|
||||||
|
fallbackNS: 'common',
|
||||||
|
resources: translations as Resource,
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false
|
||||||
|
},
|
||||||
|
detection: {
|
||||||
|
order: ['querystring', 'navigator'],
|
||||||
|
lookupQuerystring: 'lng'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.i18n.addResourceBundle('en', 'translation', en);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeLanguage(language: string) {
|
||||||
|
this.i18n.changeLanguage(language);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { I18nService } from './i18n-service';
|
||||||
|
import { I18NextTranslationService } from './translation-service';
|
||||||
|
import type { TType } from './translation-service';
|
||||||
|
import type { Readable, Writable } from 'svelte/store';
|
||||||
|
import { getContext, setContext } from 'svelte';
|
||||||
|
|
||||||
|
export const initLocalizationContext = () => {
|
||||||
|
// Initialize our services
|
||||||
|
const i18n = new I18nService();
|
||||||
|
const tranlator = new I18NextTranslationService(i18n);
|
||||||
|
|
||||||
|
// Setting the Svelte context
|
||||||
|
setLocalization({
|
||||||
|
t: tranlator.translate,
|
||||||
|
currentLanguage: tranlator.locale
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
i18n
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const CONTEXT_KEY = 't';
|
||||||
|
|
||||||
|
export type I18nContext = {
|
||||||
|
t: Readable<TType>;
|
||||||
|
currentLanguage: Writable<string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setLocalization = (context: I18nContext) => {
|
||||||
|
return setContext<I18nContext>(CONTEXT_KEY, context);
|
||||||
|
};
|
||||||
|
|
||||||
|
// To make retrieving the t function easier.
|
||||||
|
export const getLocalization = () => {
|
||||||
|
return getContext<I18nContext>(CONTEXT_KEY);
|
||||||
|
};
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"index_page": {
|
||||||
|
"slogan": "The open-source quiz-platform!",
|
||||||
|
"meta": {
|
||||||
|
"description": "ClassQuiz is a quiz app like KAHOOT! for students, which is open source and free to use",
|
||||||
|
"title": "Home"
|
||||||
|
},
|
||||||
|
"stats": "Stats",
|
||||||
|
"features": "Features",
|
||||||
|
"features_description": {
|
||||||
|
"1": "ClassQuiz is a quiz-platform that allows you to create and manage quizzes.",
|
||||||
|
"2": "The main feature is a KAHOOT!-import function that allows you to import quizzes from KAHOOT!-quizzes."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overview_page": {
|
||||||
|
"create": "Create",
|
||||||
|
"import": "Import",
|
||||||
|
"logout": "Logout",
|
||||||
|
"title": "Title",
|
||||||
|
"created_at": "Created at",
|
||||||
|
"question_count": "Question count",
|
||||||
|
"play": "Play",
|
||||||
|
"edit": "Edit",
|
||||||
|
"delete": "Delete",
|
||||||
|
"public": "Public",
|
||||||
|
"start": "Start"
|
||||||
|
},
|
||||||
|
"edit_page": {
|
||||||
|
"save": "Save",
|
||||||
|
"success_update_title": "Successfully updated quiz!",
|
||||||
|
"success_update_body": "Updated the quiz successfully!",
|
||||||
|
"close": "Close"
|
||||||
|
},
|
||||||
|
"create_page": {
|
||||||
|
"success": {
|
||||||
|
"title": "Successfully created quiz!",
|
||||||
|
"body": "Created the quiz successfully!"
|
||||||
|
},
|
||||||
|
"close": "Close"
|
||||||
|
},
|
||||||
|
"register_page": {
|
||||||
|
"greeting": "Nice to meet you!",
|
||||||
|
"create_account": "Create account",
|
||||||
|
"email": "Email",
|
||||||
|
"username": "Username",
|
||||||
|
"password": "Password",
|
||||||
|
"forgot_password?": "Forgot password?",
|
||||||
|
"register": "Register",
|
||||||
|
"already_have_account?": "Already have an account?",
|
||||||
|
"login": "Login"
|
||||||
|
},
|
||||||
|
"login_page": {
|
||||||
|
"welcome_back": "Welcome back!",
|
||||||
|
"login_or_create_account": "Login or create an account",
|
||||||
|
"already_have_account": "Don't have an account?",
|
||||||
|
"modal": {
|
||||||
|
"success": {
|
||||||
|
"success_check_mail": "Login Succeded! Check your mailbox!",
|
||||||
|
"success": "Login Succeded!",
|
||||||
|
"description": {
|
||||||
|
"success_check_mail": "Please check your mailbox, since you should have received a mail, with a link you can click to login.",
|
||||||
|
"success": "You've successfully logged in!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"wrong_creds": "Wrong email or password!",
|
||||||
|
"unexpected": "Unexpected error!",
|
||||||
|
"description": {
|
||||||
|
"wrong_creds": "Please make sure that your password and your email are correct!",
|
||||||
|
"unexpected": "There was the good old unexpected error!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import type { I18nService } from './i18n-service';
|
||||||
|
import { derived, writable } from 'svelte/store';
|
||||||
|
import type { Readable, Writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export type TType = (text: string, replacements?: Record<string, unknown>) => string;
|
||||||
|
|
||||||
|
export interface TranslationService {
|
||||||
|
locale: Writable<string>;
|
||||||
|
translate: Readable<TType>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class I18NextTranslationService implements TranslationService {
|
||||||
|
public locale: Writable<string>;
|
||||||
|
public translate: Readable<TType>;
|
||||||
|
|
||||||
|
constructor(i18n: I18nService) {
|
||||||
|
i18n.initialize();
|
||||||
|
this.locale = this.createLocale(i18n);
|
||||||
|
this.translate = this.createTranslate(i18n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locale initialization.
|
||||||
|
// 1. Create a writable store
|
||||||
|
// 2. Create a new set function that changes the i18n locale.
|
||||||
|
// 3. Create a new update function that changes the i18n locale.
|
||||||
|
// 4. Return modified writable.
|
||||||
|
private createLocale(i18n: I18nService) {
|
||||||
|
const { subscribe, set, update } = writable<string>(i18n.i18n.language);
|
||||||
|
|
||||||
|
const setLocale = (newLocale: string) => {
|
||||||
|
i18n.changeLanguage(newLocale);
|
||||||
|
set(newLocale);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLocale = (updater: (value: string) => string) => {
|
||||||
|
update((currentValue) => {
|
||||||
|
const nextLocale = updater(currentValue);
|
||||||
|
i18n.changeLanguage(nextLocale);
|
||||||
|
return nextLocale;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
update: updateLocale,
|
||||||
|
set: setLocale
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a translate function.
|
||||||
|
// It is derived from the "locale" writable.
|
||||||
|
// This means it will be updated every time the locale changes.
|
||||||
|
private createTranslate(i18n: I18nService) {
|
||||||
|
return derived([this.locale], () => {
|
||||||
|
return (key: string, replacements?: Record<string, unknown>) =>
|
||||||
|
i18n.t(key, replacements);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export default {
|
||||||
|
en: {
|
||||||
|
common: {
|
||||||
|
'ClassQuiz is a quiz app like KAHOOT! for students, which is open source and free to use.':
|
||||||
|
'ClassQuiz is a quiz app like KAHOOT! for students, which is open source and free to use.',
|
||||||
|
'The open-source quiz-platform!': 'The open-source quiz-platform!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
import { navbarVisible } from '$lib/stores.ts';
|
import { navbarVisible } from '$lib/stores.ts';
|
||||||
import * as Sentry from '@sentry/browser';
|
import * as Sentry from '@sentry/browser';
|
||||||
import { BrowserTracing } from '@sentry/tracing';
|
import { BrowserTracing } from '@sentry/tracing';
|
||||||
|
import { initLocalizationContext } from '$lib/i18n';
|
||||||
|
initLocalizationContext();
|
||||||
|
|
||||||
if (import.meta.env.VITE_SENTRY !== null) {
|
if (import.meta.env.VITE_SENTRY !== null) {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
|
|||||||
@@ -13,6 +13,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { navbarVisible } from '$lib/stores';
|
import { navbarVisible } from '$lib/stores';
|
||||||
import { browser } from '$app/env';
|
import { browser } from '$app/env';
|
||||||
|
import { getLocalization } from '$lib/i18n';
|
||||||
|
|
||||||
|
const { t } = getLocalization();
|
||||||
|
|
||||||
navbarVisible.set(true);
|
navbarVisible.set(true);
|
||||||
|
|
||||||
@@ -84,11 +87,11 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<h3 class="mt-1 text-xl font-medium text-center text-gray-600 dark:text-gray-200">
|
<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>
|
</h3>
|
||||||
|
|
||||||
<p class="mt-1 text-center text-gray-500 dark:text-gray-400">
|
<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>
|
</p>
|
||||||
|
|
||||||
<form on:submit|preventDefault={login}>
|
<form on:submit|preventDefault={login}>
|
||||||
@@ -101,13 +104,13 @@
|
|||||||
name="email"
|
name="email"
|
||||||
type="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"
|
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
|
<label
|
||||||
for="email"
|
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"
|
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>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -119,13 +122,13 @@
|
|||||||
type="password"
|
type="password"
|
||||||
bind:value={loginData.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"
|
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
|
<label
|
||||||
for="password"
|
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"
|
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>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -134,7 +137,7 @@
|
|||||||
<a
|
<a
|
||||||
href="/account/reset-password"
|
href="/account/reset-password"
|
||||||
class="text-sm text-gray-600 dark:text-gray-200 hover:text-gray-500"
|
class="text-sm text-gray-600 dark:text-gray-200 hover:text-gray-500"
|
||||||
>Forgot Password?</a
|
>{$t('register_page.forgot_password?')}</a
|
||||||
>
|
>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -157,7 +160,7 @@
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
{:else}
|
{:else}
|
||||||
Login
|
{$t('register_page.login')}
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -169,13 +172,13 @@
|
|||||||
class="flex items-center justify-center py-4 text-center bg-gray-50 dark:bg-gray-700"
|
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"
|
<span class="text-sm text-gray-600 dark:text-gray-200"
|
||||||
>Don't have an account?
|
>{$t('login_page.already_have_account')}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="/account/register"
|
href="/account/register"
|
||||||
class="mx-2 text-sm font-bold text-blue-500 dark:text-blue-400 hover:underline"
|
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>
|
||||||
</div>
|
</div>
|
||||||
@@ -267,13 +270,13 @@
|
|||||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
<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">
|
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
||||||
{#if responseData.data === 'magic'}
|
{#if responseData.data === 'magic'}
|
||||||
Login Succeded! Check your mailbox!
|
{$t('login_page.modal.success.success_check_mail')}
|
||||||
{:else if responseData.data === 'password'}
|
{:else if responseData.data === 'password'}
|
||||||
Login Succeded!
|
{$t('login_page.modal.success.success')}
|
||||||
{:else if responseData.data === '404'}
|
{:else if responseData.data === '404'}
|
||||||
Wrong email or password!
|
{$t('login_page.modal.error.wrong_creds')}
|
||||||
{:else if responseData.data === 'error'}
|
{:else if responseData.data === 'error'}
|
||||||
unexpected error!
|
{$t('login_page.modal.error.unexpected')}
|
||||||
{:else}
|
{:else}
|
||||||
You stupid Mawoka!
|
You stupid Mawoka!
|
||||||
{/if}
|
{/if}
|
||||||
@@ -281,14 +284,13 @@
|
|||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<p class="text-sm text-gray-500">
|
<p class="text-sm text-gray-500">
|
||||||
{#if responseData.data === 'magic'}
|
{#if responseData.data === 'magic'}
|
||||||
Please check your mailbox, since you should have received a
|
{$t('login_page.modal.success.description.success_check_mail')}
|
||||||
Mail, with a link you can click to login.
|
|
||||||
{:else if responseData.data === 'password'}
|
{:else if responseData.data === 'password'}
|
||||||
You've successfully logged in!
|
{$t('login_page.modal.success.description.success')}
|
||||||
{:else if responseData.data === '404'}
|
{: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'}
|
{:else if responseData.data === 'error'}
|
||||||
There was the good old unexpected error!
|
{$t('login_page.modal.error.description.unexpected')}
|
||||||
{:else}
|
{:else}
|
||||||
You stupid Mawoka!
|
You stupid Mawoka!
|
||||||
{/if}
|
{/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"
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,9 +12,13 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createForm } from 'felte';
|
import { createForm } from 'felte';
|
||||||
// import reporter from '@felte/reporter-tippy';
|
import { getLocalization } from '$lib/i18n';
|
||||||
import { validateSchema } from '@felte/validator-yup';
|
import { validateSchema } from '@felte/validator-yup';
|
||||||
import { navbarVisible } from '$lib/stores';
|
import { navbarVisible } from '$lib/stores';
|
||||||
|
|
||||||
|
const { t } = getLocalization();
|
||||||
|
// import reporter from '@felte/reporter-tippy';
|
||||||
|
|
||||||
navbarVisible.set(true);
|
navbarVisible.set(true);
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
|
|
||||||
@@ -83,10 +87,12 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<h3 class="mt-1 text-xl font-medium text-center text-gray-600 dark:text-gray-200">
|
<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>
|
</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>
|
<form use:form>
|
||||||
<div class="w-full mt-4">
|
<div class="w-full mt-4">
|
||||||
@@ -97,7 +103,7 @@
|
|||||||
name="email"
|
name="email"
|
||||||
type="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"
|
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-red-700={$errors.email !== null}
|
||||||
class:ring-green-600={$touched.email === true &&
|
class:ring-green-600={$touched.email === true &&
|
||||||
$errors.email === null}
|
$errors.email === null}
|
||||||
@@ -106,7 +112,7 @@
|
|||||||
for="email"
|
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"
|
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>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,7 +123,7 @@
|
|||||||
name="username"
|
name="username"
|
||||||
type="text"
|
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"
|
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-red-700={$errors.username !== null}
|
||||||
class:ring-green-600={$touched.username === true &&
|
class:ring-green-600={$touched.username === true &&
|
||||||
$errors.username === null}
|
$errors.username === null}
|
||||||
@@ -126,7 +132,7 @@
|
|||||||
for="username"
|
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"
|
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>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -140,13 +146,13 @@
|
|||||||
class:ring-green-600={$touched.password1 === true &&
|
class:ring-green-600={$touched.password1 === true &&
|
||||||
$errors.password1 === null}
|
$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"
|
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
|
<label
|
||||||
for="password1"
|
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"
|
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>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -157,7 +163,7 @@
|
|||||||
name="password2"
|
name="password2"
|
||||||
type="password"
|
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"
|
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-red-700={$errors.password2 !== null}
|
||||||
class:ring-green-600={$touched.password2 === true &&
|
class:ring-green-600={$touched.password2 === true &&
|
||||||
$errors.password2 === null}
|
$errors.password2 === null}
|
||||||
@@ -166,7 +172,7 @@
|
|||||||
for="password2"
|
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"
|
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>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -175,7 +181,7 @@
|
|||||||
<a
|
<a
|
||||||
href="/account/reset-password"
|
href="/account/reset-password"
|
||||||
class="text-sm text-gray-600 dark:text-gray-200 hover:text-gray-500"
|
class="text-sm text-gray-600 dark:text-gray-200 hover:text-gray-500"
|
||||||
>Forgot Password?</a
|
>{$t('register_page.forgot_password?')}</a
|
||||||
>
|
>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -197,7 +203,7 @@
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
{:else}
|
{:else}
|
||||||
Register
|
{$t('register_page.register')}
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -209,13 +215,13 @@
|
|||||||
class="flex items-center justify-center py-4 text-center bg-gray-50 dark:bg-gray-700"
|
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"
|
<span class="text-sm text-gray-600 dark:text-gray-200"
|
||||||
>Already have an account?
|
>{$t('register_page.already_have_account?')}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="/account/login"
|
href="/account/login"
|
||||||
class="mx-2 text-sm font-bold text-blue-500 dark:text-blue-400 hover:underline"
|
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>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import Editor from '$lib/editor.svelte';
|
import Editor from '$lib/editor.svelte';
|
||||||
|
import { getLocalization } from '$lib/i18n';
|
||||||
|
|
||||||
|
const { t } = getLocalization();
|
||||||
|
|
||||||
interface Data {
|
interface Data {
|
||||||
public: boolean;
|
public: boolean;
|
||||||
@@ -134,10 +137,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
<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">
|
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
||||||
Successfully created quiz!
|
{$t('create_page.success.title')}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="mt-2">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -149,7 +152,7 @@
|
|||||||
window.location.href = '/overview';
|
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"
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
href="https://create.kahoot.it/discover"
|
href="https://create.kahoot.it/discover"
|
||||||
rel="nofollow"
|
rel="nofollow"
|
||||||
target="_blank">create.kahoot.it/discover</a
|
target="_blank">create.kahoot.it/discover</a
|
||||||
>, find the quiz you want to import, click on it and copy the URL into your clipboard.
|
>, find the quiz you want to import, click on it and copy the URL into your clipboard. Then
|
||||||
Then go to
|
go to
|
||||||
<a href="https://classquiz.mawoka.eu/import" rel="nofollow" target="_blank"
|
<a href="https://classquiz.mawoka.eu/import" rel="nofollow" target="_blank"
|
||||||
>classquiz.mawoka.eu/import</a
|
>classquiz.mawoka.eu/import</a
|
||||||
> and paste the url into the text field.
|
> and paste the url into the text field.
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Editor from '$lib/editor.svelte';
|
import Editor from '$lib/editor.svelte';
|
||||||
|
import { getLocalization } from '$lib/i18n';
|
||||||
|
|
||||||
|
const { t } = getLocalization();
|
||||||
|
|
||||||
let responseData = {
|
let responseData = {
|
||||||
open: false,
|
open: false,
|
||||||
@@ -88,7 +91,7 @@
|
|||||||
{:then lol}
|
{:then lol}
|
||||||
{#if data !== undefined}
|
{#if data !== undefined}
|
||||||
<form on:submit|preventDefault={submit} class="grid grid-cols-1 gap-2">
|
<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>
|
</form>
|
||||||
{/if}
|
{/if}
|
||||||
{:catch err}
|
{:catch err}
|
||||||
@@ -141,10 +144,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
<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">
|
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
||||||
Successfully updated quiz!
|
{$t('edit_page.success_update_title')}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="mt-2">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -156,7 +161,7 @@
|
|||||||
window.location.href = '/overview';
|
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"
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { navbarVisible } from '$lib/stores';
|
import { navbarVisible } from '$lib/stores';
|
||||||
|
import { getLocalization } from '$lib/i18n';
|
||||||
|
|
||||||
|
const { t } = getLocalization();
|
||||||
|
|
||||||
navbarVisible.set(true);
|
navbarVisible.set(true);
|
||||||
|
|
||||||
@@ -15,26 +18,23 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>ClassQuiz - Home</title>
|
<title>ClassQuiz - {$t('index_page.meta.title')}</title>
|
||||||
<meta
|
<meta name="description" content={$t('index_page.meta.description')} />
|
||||||
name="description"
|
|
||||||
content="ClassQuiz is a quiz app like KAHOOT! for students, which is open source and free to use."
|
|
||||||
/>
|
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="pt-12 text-center">
|
<div class="pt-12 text-center">
|
||||||
<h1 class="sm:text-8xl text-6xl mt-6 marck-script">ClassQuiz</h1>
|
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="stats">
|
<section id="stats">
|
||||||
<div class="text-center pt-48 snap-y">
|
<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">
|
<p class="text-xl pt-4">
|
||||||
{#await getStats() then stats}
|
{#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.
|
<span class="underline">{stats.quiz_count}</span> quizzes on ClassQuiz.
|
||||||
{/await}
|
{/await}
|
||||||
</p>
|
</p>
|
||||||
@@ -42,11 +42,11 @@
|
|||||||
</section>
|
</section>
|
||||||
<section id="features">
|
<section id="features">
|
||||||
<div class="text-center pt-24 snap-y">
|
<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">
|
<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 />
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { QuizData } from '../app';
|
import type { QuizData } from '../app';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
import { getLocalization } from '$lib/i18n';
|
||||||
|
const { t } = getLocalization();
|
||||||
|
|
||||||
const getData = async (): Promise<Array<QuizData>> => {
|
const getData = async (): Promise<Array<QuizData>> => {
|
||||||
const res = await fetch('/api/v1/quiz/list');
|
const res = await fetch('/api/v1/quiz/list');
|
||||||
@@ -76,17 +78,17 @@
|
|||||||
<a
|
<a
|
||||||
href="/create"
|
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"
|
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>
|
||||||
<a
|
<a
|
||||||
href="/import"
|
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"
|
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>
|
||||||
<a
|
<a
|
||||||
href="/api/v1/users/logout"
|
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"
|
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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-x-auto sm:-mx-8 lg:-mx-8">
|
<div class="overflow-x-auto sm:-mx-8 lg:-mx-8">
|
||||||
@@ -99,43 +101,43 @@
|
|||||||
scope="col"
|
scope="col"
|
||||||
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
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>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
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>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
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>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
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>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
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>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
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>
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
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>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -167,7 +169,7 @@
|
|||||||
}}
|
}}
|
||||||
class="border border-green-600"
|
class="border border-green-600"
|
||||||
>
|
>
|
||||||
Start
|
{$t('overview_page.start')}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
@@ -184,7 +186,7 @@
|
|||||||
}}
|
}}
|
||||||
class="border border-green-600"
|
class="border border-green-600"
|
||||||
>
|
>
|
||||||
Delete
|
{$t('overview_page.delete')}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
|
|||||||
Reference in New Issue
Block a user