feat: multi language support (#254)

* feat: multitranslational support

* - completion of settings page
- completion of sidebar

* sidebar rerendering bug fix

* completion of the lesson navigation & materials page

* completion of the course navigation

* completion of requested languages

* - Implementation of Language Picker
- Rename Bug Fixes

* checkpoint save

* - addition of the language picker to the onboarding.
- addition of the language switcher for the lesson notes.

* merge completion

* initialization of the new branch

* add transition.svelte file

* lesson note bug fix

* feat: add translation migration

* completion of translation

* fix height issue of dropdown

* refactoring

* fix: improve code with LOCALE from db

* finally get it fully functional

* connect with AI

* backward compatibility for lesson note

* preserve old content, when generating AI content

* undo translations in upgrade modal

* fix duplicate key issue from codacy trigger

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
This commit is contained in:
Adeyemi Adetunji
2024-04-19 05:45:48 +01:00
committed by GitHub
co-authored by Rotimi Best
parent ff7d2cee35
commit d75e2f258e
153 changed files with 11264 additions and 1130 deletions
@@ -10,6 +10,13 @@
import { profile } from '$lib/utils/store/user';
import { NavClasses } from '$lib/utils/constants/reusableClass';
import { sideBar } from '$lib/components/Org/store';
import { t } from '$lib/utils/functions/translations';
interface sideLinks {
name: string;
icon: any;
link: string;
}
function isActive(pagePath: string, itemPath: string) {
const pageLinkItems = pagePath.split('/');
@@ -21,24 +28,27 @@
return pagePath.includes(itemPath);
}
let sideLinks = [
let sideLinks: sideLinks[] = [];
$: sideLinks = [
{
name: 'Home',
name: $t('lms_navigation.home'),
icon: HomeIcon,
link: '/lms'
},
{
name: 'My Learning',
name: $t('lms_navigation.my_learning'),
icon: CourseIcon,
link: '/lms/mylearning'
},
{
name: 'Exercise',
name: $t('lms_navigation.exercise'),
icon: LicenseDraft,
link: '/lms/exercises'
},
{
name: 'Community',
name: $t('lms_navigation.community'),
icon: CommunityIcon,
link: '/lms/community'
}
@@ -98,7 +108,7 @@
<a href="/lms" class="text-black" on:click={toggleSidebar}>
<li class="flex items-center py-3 px-4 mb-2 rounded">
<HelpIcon size={20} class="carbon-icon dark:text-white" />
<p class="dark:text-white ml-2">Help</p>
<p class="dark:text-white ml-2">{$t('lms_navigation.help')}</p>
</li>
</a>
<a href="/lms/settings" class="text-black" on:click={toggleSidebar}>
@@ -109,7 +119,7 @@
) && NavClasses.active}"
>
<Settings size={20} class="carbon-icon dark:text-white" />
<p class="dark:text-white ml-2">Settings</p>
<p class="dark:text-white ml-2">{$t('lms_navigation.settings')}</p>
</li>
</a>
</ul>
@@ -4,6 +4,7 @@
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import CoursesEmptyIcon from '$lib/components/Icons/CoursesEmptyIcon.svelte';
import { coursesInProgress } from '$lib/components/Courses/store';
import { t } from '$lib/utils/functions/translations';
const gotoCourse = (id: string | undefined) => {
if (!id) return;
@@ -12,7 +13,9 @@
</script>
<section class="h-full">
<p class="text-base font-semibold text-[#040F2D] pb-3 dark:text-white">Currently Learning</p>
<p class="text-base font-semibold text-[#040F2D] pb-3 dark:text-white">
{$t('dashboard.current_lesson')}
</p>
<div
class="flex items-center flex-col border border-[#EAEAEA] dark:bg-neutral-800 gap-2 rounded w-full lg:h-[40vh] p-3"
>
@@ -33,7 +36,7 @@
</p>
</div>
<PrimaryButton
label="Continue"
label={$t('dashboard.continue')}
variant={VARIANTS.OUTLINED}
className="rounded-none text-[#0233BD]"
onClick={() => gotoCourse(course.id)}
@@ -53,8 +56,8 @@
{:else}
<div class="flex flex-col items-center">
<CoursesEmptyIcon />
<h3 class="text-center">No courses in progress yet</h3>
<p class="w-[75%] text-center">Once you start a course, your progress will reflect here</p>
<h3 class="text-center">{$t('dashboard.no_courses')}</h3>
<p class="w-[75%] text-center">{$t('dashboard.start_course')}</p>
</div>
{/if}
</div>
@@ -9,9 +9,14 @@
import { snackbar } from '$lib/components/Snackbar/store';
import LogoutButton from '$lib/components/Buttons/Logout/index.svelte';
import generateUUID from '$lib/utils/functions/generateUUID';
import { t } from '$lib/utils/functions/translations';
import LanguagePicker from '$lib/components/Org/Settings/LanguagePicker.svelte';
import { handleLocaleChange } from '$lib/utils/functions/translations';
let avatar = '';
let loading = false;
let hasLangChanged = false;
let locale = '';
async function handleUpdate() {
try {
@@ -20,7 +25,8 @@
const updates = {
fullname: $profile.fullname,
username: $profile.username,
email: $profile.email
email: $profile.email,
locale
};
if (avatar) {
@@ -47,25 +53,31 @@
}));
snackbar.success();
if (hasLangChanged) {
handleLocaleChange(locale);
}
if (error) throw error;
} catch (error) {
let message = error.message;
if (message.includes('profile_username_key')) {
message = 'username already exists';
message = $t('username already exists');
}
snackbar.error(`Update failed: ${message}`);
snackbar.error(`snackbar.lms.error.update: ${message}`);
loading = false;
} finally {
loading = false;
}
}
$: locale = $profile.locale;
</script>
<Grid class="border-c rounded border-gray-200 dark:border-neutral-600 w-full mt-5">
<Row class="flex flex-col lg:flex-row items-center lg:items-start py-7 border-bottom-c ">
<Column sm={4} md={8} lg={4} class="mt-2 md:mt-0">
<p class="dark:text-white font-bold">Profile Picture</p>
<p class="dark:text-white font-bold">{$t('settings.profile.profile_picture.heading')}</p>
</Column>
<Column sm={2} md={4} lg={8} class="mt-2 lg:mt-0">
<UploadImage bind:avatar src={$profile.avatar_url} widthHeight="w-16 h-16 lg:w-24 lg:h-24" />
@@ -73,17 +85,35 @@
</Row>
<Row class="flex flex-col lg:flex-row py-7 border-bottom-c">
<Column sm={4} md={4} lg={4}>
<p class="dark:text-white font-bold">Personal Information</p>
<p class="dark:text-white font-bold">{$t('settings.profile.personal_information.heading')}</p>
</Column>
<Column sm={8} md={8} lg={8} class="mt-2 lg:mt-0">
<TextField label="Full Name" bind:value={$profile.fullname} className="w-full lg:w-60 mb-4" />
<TextField label="Username" bind:value={$profile.username} className="w-full lg:w-60 mb-4" />
<TextField label="Email" bind:value={$profile.email} className="w-full lg:w-60 mb-4" />
<TextField
label={$t('settings.profile.personal_information.full_name')}
bind:value={$profile.fullname}
className="w-full lg:w-60 mb-4"
/>
<TextField
label={$t('settings.profile.personal_information.username')}
bind:value={$profile.username}
className="w-full lg:w-60 mb-4"
/>
<TextField
label={$t('settings.profile.personal_information.email')}
bind:value={$profile.email}
className="w-full lg:w-60 mb-4"
/>
<LanguagePicker
bind:hasLangChanged
bind:value={$profile.locale}
className="w-full lg:w-60 mb-4"
/>
</Column>
</Row>
<Row class="m-5 w-full flex items-center gap-2 lg:justify-center">
<PrimaryButton
label="Update Profile"
label={$t('settings.profile.update_profile')}
variant={VARIANTS.CONTAINED_DARK}
className="mr-5"
isLoading={loading}
@@ -4,6 +4,7 @@
import { isMobile } from '$lib/utils/store/useMobile';
import { formatDate } from '$lib/utils/functions/routes/dashboard';
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
import { t } from '$lib/utils/functions/translations';
let store;
@@ -26,7 +27,7 @@
</script>
<section class="w-full h-full mt-2">
<p class="text-base font-semibold text-[#040F2D] pb-3 dark:text-white">Your Schedule</p>
<p class="text-base font-semibold text-[#040F2D] pb-3 dark:text-white">{$t('schedule.title')}</p>
<div
class="flex items-center flex-col border border-[#EAEAEA] dark:bg-neutral-800 rounded w-full h-fit lg:h-[40vh] p-3 overflow-y-auto"
@@ -51,23 +52,23 @@
{formatDate($store?.selected)}
</p>
{/if}
{#if formatDate($store?.selected) == 'Today'}
{#if formatDate($store?.selected) == $t('dashboard.today')}
<span class="flex flex-col md:flex-row items-baseline justify-center gap-10 mt-5">
<p class="text-xs font-normal text-[#656565] dark:text-white">2:30pm</p>
<div class="flex flex-col items-start gap-4">
<span>
<p class="text-lg font-semibold text-[#262626] dark:text-white">
The essence of design collaboration
{$t('schedule.essence')}
</p>
<a href="#" class="text-sm font-normal text-primary-700 dark:text-white"
>Figma Introduction</a
>{$t('schedule.figma')}</a
>
</span>
<PrimaryButton label="Join Call" variant={VARIANTS.OUTLINED} className="" />
<PrimaryButton label={$t('schedule.join')} variant={VARIANTS.OUTLINED} className="" />
</div>
</span>
{:else}
<p>No Lesson on this day</p>
<p>{$t('schedule.no')}</p>
{/if}
</div>
</section>