docs: how to translate dashboard (#292)

* add cloud translation api package

* feat: Script that auto generates translations for other languages

* add comments

* fix: broken translation

* improve translation

* update locck file

* clean up

* all round trasnlations check

* checkpoint

* finish up

* move to dev dependencies

* fix vercel build error

* translations fix

* - add documentation
- translation bug fix

* quick adjust

* PR Fixes

* add new line

* update docs

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
This commit is contained in:
Adeyemi Adetunji
2024-04-26 17:54:11 +01:00
committed by GitHub
co-authored by Rotimi Best
parent 96cbff675d
commit 3a5d13ea41
35 changed files with 7638 additions and 9046 deletions
@@ -6,6 +6,7 @@
import IconButton from '$lib/components/IconButton/index.svelte';
import { Loading } from 'carbon-components-svelte';
import HtmlRender from '$lib/components/HTMLRender/HTMLRender.svelte';
import { t } from '$lib/utils/functions/translations';
export let className = '';
export let handleInsert = (v: string) => {};
@@ -39,7 +40,7 @@
function callAI(prompt: string, rephrase: boolean) {
isSubmitted = true;
$input = rephrase ? `Can you please rephrase this: ${plainText}` : prompt;
$input = rephrase ? `${$t('ai.can_you')}: ${plainText}` : prompt;
setTimeout(() => {
handleSubmit({ preventDefault: () => {} });
@@ -91,7 +92,7 @@
{#if $isLoading}
<Loading withOverlay={false} small />
{:else}
Insert
{$t('ai.insert')}
{/if}
</button>
<button
@@ -101,7 +102,7 @@
type="button"
disabled={$isLoading}
>
Rephrase
{$t('ai.rephrase')}
</button>
{#if !$isLoading}
<button
@@ -109,25 +110,25 @@
on:click|preventDefault={restart}
type="button"
>
Reset
{$t('ai.reset')}
</button>
{/if}
</div>
</div>
{:else}
<div class="h-full">
<p class="text-sm font-medium mb-2 ml-1">Help me write</p>
<p class="text-sm font-medium mb-2 ml-1">{$t('ai.help_me')}</p>
<textarea
bind:value={prompt}
class="w-full border-0 rounded-lg h-[65%] text-sm m-0 dark:text-black"
placeholder="Help me write a short description"
placeholder={$t('ai.placeholder')}
/>
<button
class="flex text-xs px-4 py-2 m-0 border rounded-md font-medium"
on:click|preventDefault={() => callAI(prompt, false)}
type="button"
>
Start typing...
{$t('ai.text')}
</button>
</div>
{/if}
@@ -47,7 +47,7 @@
/>
</svelte:head>
<UpgradeBanner>Upgrade your plan to generate certificates</UpgradeBanner>
<UpgradeBanner>{$t('upgrade.certificate')}</UpgradeBanner>
<main class="md:-ml-3 md:-mr-3 px-2 mt-2">
<div class="flex-1 flex flex-col lg:flex-row justify-between gap-3 w-full mb-3 h-4/5">
@@ -10,8 +10,8 @@
{$t('course.navItem.certificates.locked_certificate')}
</p>
<p class="text-sm font-normal text-center">
{$t('course.navItem.certificates.locked_certificate_body_1')} <br />
{$t('course.navItem.certificates.locked_certificate_body_2')}
{$t('course.navItem.certificates.instructor_unlock')} <br />
{$t('course.navItem.certificates.contact_instructor')}
</p>
</div>
</Box>
@@ -41,20 +41,24 @@
const options = [
{
title: $t('course.navItem.lessons.exercises.new_exercise_modal.options.1'),
subtitle: $t('course.navItem.lessons.exercises.new_exercise_modal.options.subtitle_1'),
title: $t('course.navItem.lessons.exercises.new_exercise_modal.options.from_scratch'),
subtitle: $t(
'course.navItem.lessons.exercises.new_exercise_modal.options.from_scratch_subtitle'
),
type: Type.SCRATCH,
isDisabled: false
},
{
title: $t('course.navItem.lessons.exercises.new_exercise_modal.options.2'),
subtitle: $t('course.navItem.lessons.exercises.new_exercise_modal.options.subtitle_2'),
title: $t('course.navItem.lessons.exercises.new_exercise_modal.options.use_template'),
subtitle: $t(
'course.navItem.lessons.exercises.new_exercise_modal.options.use_template_subtitle'
),
type: Type.TEMPLATE,
isDisabled: false
},
{
title: $t('course.navItem.lessons.exercises.new_exercise_modal.options.3'),
subtitle: $t('course.navItem.lessons.exercises.new_exercise_modal.options.subtitle_3'),
title: $t('course.navItem.lessons.exercises.new_exercise_modal.options.use_ai'),
subtitle: $t('course.navItem.lessons.exercises.new_exercise_modal.options.use_ai_subtitle'),
type: Type.AI,
isDisabled: false
}
@@ -177,8 +177,8 @@
{$t('course.navItem.lessons.materials.tabs.video.add_video.oops')}
</h3>
<p class="text-center text-xs font-normal text-[#ADADAD]">
{$t('course.navItem.lessons.materials.tabs.video.add_video.sorry')}<br />
{$t('course.navItem.lessons.materials.tabs.video.add_video.sorry_2')}
{$t('course.navItem.lessons.materials.tabs.video.add_video.big_file')}<br />
{$t('course.navItem.lessons.materials.tabs.video.add_video.maximum_size')}
</p>
</span>
<PrimaryButton
@@ -194,8 +194,8 @@
{$t('course.navItem.lessons.materials.tabs.video.add_video.oops')}
</h3>
<p class="text-center text-xs font-normal text-[#ADADAD]">
{$t('course.navItem.lessons.materials.tabs.video.add_video.sorry_3')}<br />
{$t('course.navItem.lessons.materials.tabs.video.add_video.sorry_4')}
{$t('course.navItem.lessons.materials.tabs.video.add_video.unsupported_format')}<br />
{$t('course.navItem.lessons.materials.tabs.video.add_video.format')}
</p>
</span>
<PrimaryButton
@@ -102,7 +102,7 @@
if (updateError) {
console.error('Error updating translation:', updateError.message);
snackbar.error('Updating translations failed');
snackbar.error('snackbar.materials.update_translations');
}
} else {
const { error: insertError } = await supabase.from('lesson_language').insert({
@@ -113,7 +113,7 @@
if (insertError) {
console.error('Error inserting translation:', insertError.message);
snackbar.error('Creating new translations failed');
snackbar.error('snackbar.materials.creating_new');
return;
}
@@ -138,13 +138,13 @@
$: {
navItems = [
{
label: $t('course.navItems.1'),
label: $t('course.navItems.nav_news_feed'),
to: getNavItemRoute($course.id),
hideSortIcon: true,
isPaidFeature: false
},
{
label: $t('course.navItems.2'),
label: $t('course.navItems.nav_lessons'),
to: getLessonsRoute($course.id),
hideSortIcon: false,
isPaidFeature: false,
@@ -152,13 +152,13 @@
isExpanded: isStudent ? true : $page.url.pathname.includes('/lessons')
},
{
label: $t('course.navItems.3'),
label: $t('course.navItems.nav_attendance'),
to: getNavItemRoute($course.id, 'attendance'),
isPaidFeature: false,
hideSortIcon: true
},
{
label: $t('course.navItems.4'),
label: $t('course.navItems.nav_submissions'),
to: getNavItemRoute($course.id, 'submissions'),
hideSortIcon: true,
isPaidFeature: false,
@@ -167,13 +167,13 @@
}
},
{
label: $t('course.navItems.5'),
label: $t('course.navItems.nav_marks'),
to: getNavItemRoute($course.id, 'marks'),
isPaidFeature: false,
hideSortIcon: true
},
{
label: $t('course.navItems.6'),
label: $t('course.navItems.nav_people'),
to: getNavItemRoute($course.id, 'people'),
isPaidFeature: false,
hideSortIcon: true,
@@ -182,7 +182,7 @@
}
},
{
label: $t('course.navItems.7'),
label: $t('course.navItems.nav_certificates'),
to: getNavItemRoute($course.id, 'certificates'),
hideSortIcon: true,
isPaidFeature: true,
@@ -195,7 +195,7 @@
}
},
{
label: $t('course.navItems.8'),
label: $t('course.navItems.nav_landing_page'),
to: getNavItemRoute($course.id, 'landingpage'),
hideSortIcon: true,
isPaidFeature: false,
@@ -204,7 +204,7 @@
}
},
{
label: $t('course.navItems.9'),
label: $t('course.navItems.nav_settings'),
to: getNavItemRoute($course.id, 'settings'),
hideSortIcon: true,
isPaidFeature: false,
@@ -288,7 +288,7 @@
</Column>
<Column sm={8} md={8} lg={8}>
{#if $isFreePlan}
<UpgradeBanner>Upgrade to download lessons</UpgradeBanner>
<UpgradeBanner>{$t('upgrade.download_lessons')}</UpgradeBanner>
{:else}
<Toggle size="sm" bind:toggled={$settings.lesson_download}>
<span slot="labelA" style="color: gray">{$t('course.navItem.settings.disabled')}</span>
@@ -304,7 +304,7 @@
</Column>
<Column sm={8} md={8} lg={8}>
{#if $isFreePlan}
<UpgradeBanner>Upgrade to download course</UpgradeBanner>
<UpgradeBanner>{$t('upgrade.download_course')}</UpgradeBanner>
{:else}
<PrimaryButton
variant={VARIANTS.OUTLINED}
@@ -33,7 +33,7 @@
labelClassName="font-bold"
label={$t('course.navItem.landing_page.editor.header_form.short_video')}
placeholder="www.youtube.com/watch?v=uYRq60G5XTk"
helperMessage="Enter a link to a youtube video"
helperMessage={$t('course.navItem.landing_page.editor.header_form.helper')}
type="text"
bind:value={course.metadata.videoUrl}
/>
@@ -62,7 +62,7 @@
<div class="mt-5">
<label for="upload" class="font-bold"
>{$t('course.navItem.landing_page.editor.instructure_form.upload')}</label
>{$t('course.navItem.landing_page.editor.instructor_form.upload')}</label
>
<UploadImage bind:avatar src={imgUrl} />
</div>
@@ -70,22 +70,22 @@
<TextField
className="mt-5"
labelClassName="font-bold"
label={$t('course.navItem.landing_page.editor.instructure_form.name')}
placeholder={$t('course.navItem.landing_page.editor.instructure_form.name_placeholder')}
label={$t('course.navItem.landing_page.editor.instructor_form.name')}
placeholder={$t('course.navItem.landing_page.editor.instructor_form.name_placeholder')}
bind:value={name}
/>
<TextField
className="mt-5"
labelClassName="font-bold"
label={$t('course.navItem.landing_page.editor.instructure_form.role')}
label={$t('course.navItem.landing_page.editor.instructor_form.role')}
placeholder="e.g Software developer"
bind:value={role}
/>
<TextArea
label={$t('course.navItem.landing_page.editor.instructure_form.about')}
placeholder={$t('course.navItem.landing_page.editor.instructure_form.about_placeholder')}
label={$t('course.navItem.landing_page.editor.instructor_form.about')}
placeholder={$t('course.navItem.landing_page.editor.instructor_form.about_placeholder')}
rows={6}
className="mt-5"
labelClassName="font-bold"
@@ -95,7 +95,7 @@
<TextField
className="mt-5"
labelClassName="font-bold"
label={$t('course.navItem.landing_page.editor.instructure_form.total')}
label={$t('course.navItem.landing_page.editor.instructor_form.total')}
type="number"
bind:value={courseNo}
/>
@@ -11,6 +11,7 @@
import ReviewFormEditor from './ReviewFormEditor.svelte';
import Avatar from '$lib/components/Avatar/index.svelte';
import { processErrors } from '$lib/utils/functions/validator';
import { t } from '$lib/utils/functions/translations';
export let course = {};
@@ -125,7 +126,7 @@
<!-- create reviews button -->
<PrimaryButton
label="Add Reviews"
label={$t('course.navItem.landing_page.editor.reviews_form.add_reviews')}
variant={VARIANTS.CONTAINED}
onClick={addReviewForm}
className="w-5 rounded-md mt-8"
@@ -47,43 +47,43 @@
{
key: 1,
path: '',
title: $t('course.navItem.landing_page.editor.title.1')
title: $t('course.navItem.landing_page.editor.title.header')
},
{
key: 2,
path: 'metadata.requirements',
title: $t('course.navItem.landing_page.editor.title.2'),
title: $t('course.navItem.landing_page.editor.title.requirement'),
enableAIWriter: true,
initPrompt: 'Please write few requirements needed to take this course:'
initPrompt: $t('course.navItem.landing_page.editor.title.requirement')
},
{
key: 3,
path: 'metadata.description',
title: $t('course.navItem.landing_page.editor.title.3'),
title: $t('course.navItem.landing_page.editor.title.description'),
enableAIWriter: true,
initPrompt: 'Please write a detailed course description for this course:'
},
{
key: 4,
path: 'metadata.goals',
title: $t('course.navItem.landing_page.editor.title.4'),
title: $t('course.navItem.landing_page.editor.title.goals'),
enableAIWriter: true,
initPrompt: 'What should a student expect to learn from this course:'
},
{
key: 5,
path: '',
title: $t('course.navItem.landing_page.editor.title.5')
title: $t('course.navItem.landing_page.editor.title.reviews')
},
{
key: 6,
path: '',
title: $t('course.navItem.landing_page.editor.title.6')
title: $t('course.navItem.landing_page.editor.title.instructor')
},
{
key: 7,
path: '',
title: $t('course.navItem.landing_page.editor.title.7')
title: $t('course.navItem.landing_page.editor.title.pricing')
}
];
let selectedSection: Section | null = null;
@@ -13,26 +13,26 @@ export const SECTION_KEYS = {
export const NAV_ITEMS = [
{
key: SECTION_KEYS.REQUIREMENT,
label: 'course.navItem.landing_page.editor.title.2'
label: 'course.navItem.landing_page.editor.title.requirement'
},
{
key: SECTION_KEYS.DESCRIPTION,
label: 'course.navItem.landing_page.editor.title.3'
label: 'course.navItem.landing_page.editor.title.description'
},
{
key: SECTION_KEYS.GOALS,
label: 'course.navItem.landing_page.editor.title.4'
label: 'course.navItem.landing_page.editor.title.goals'
},
{
key: SECTION_KEYS.LESSONS,
label: 'course.navItems.2'
label: 'course.navItems.nav_lessons'
},
{
key: SECTION_KEYS.REVIEWS,
label: 'course.navItem.landing_page.editor.title.5'
label: 'course.navItem.landing_page.editor.title.reviews'
},
{
key: SECTION_KEYS.INSTRUCTOR,
label: 'course.navItem.landing_page.editor.title.6'
label: 'course.navItem.landing_page.editor.title.instructor'
}
];
@@ -125,7 +125,11 @@
</p>
<p class="text-xs py-2">
{#if isOnLandingPage}
<span class="px-2">{!cost ? 'Free' : formatter.format(cost)}</span>
<span class="px-2"
>{!cost
? $t('course.navItem.landing_page.pricing_section')
: formatter.format(cost)}</span
>
{:else if isLMS}
<div class="flex items-center gap-2">
<div class=" relative bg-[#EAEAEA] w-[50px] h-1">
@@ -72,15 +72,15 @@
<h3 class="font-normal text-lg">
{$t('settings.integrations.sub_heading')}
</h3>
<h4 class="font-normal">{$t('settings.integrations.step_1')}</h4>
<h4 class="font-normal">{$t('settings.integrations.step_authenticate')}</h4>
<PrimaryButton
className="mb-5"
variant={VARIANTS.OUTLINED}
onClick={() => window.open('https://t.me/classroomio_bot', '_blank')}
><span class="mr-2">{$t('settings.integrations.button_1')}</span>
><span class="mr-2">{$t('settings.integrations.open_bot_button')}</span>
<ArrowUpRightIcon /></PrimaryButton
>
<h4 class="font-normal">{$t('settings.integrations.step_2')}</h4>
<h4 class="font-normal">{$t('settings.integrations.step_chatId')}</h4>
<TextField
bind:value={chatId}
placeholder="360111"
@@ -88,7 +88,9 @@
type="number"
isRequired
/>
<PrimaryButton onClick={addChatId}>{$t('settings.integrations.button_2')}</PrimaryButton>
<PrimaryButton onClick={addChatId}
>{$t('settings.integrations.connect_button')}</PrimaryButton
>
</div>
{/if}
</Column>
@@ -45,35 +45,35 @@
tabs = [
{
key: 0,
label: $t('settings.tabs.1'),
label: $t('settings.tabs.profile_tab'),
tabKey: '',
href: $page.url.pathname,
disabled: false
},
{
key: 1,
label: $t('settings.tabs.2'),
label: $t('settings.tabs.organization_tab'),
tabKey: 'org',
href: `${$page.url.pathname}?tab=org`,
disabled: !$isOrgAdmin
},
{
key: 2,
label: $t('settings.tabs.3'),
label: $t('settings.tabs.landing_page_tab'),
tabKey: 'landingpage',
href: `${$page.url.pathname}?tab=landingpage`,
disabled: !$isOrgAdmin
},
{
key: 3,
label: $t('settings.tabs.4'),
label: $t('settings.tabs.billing_tab'),
tabKey: 'billing',
href: `${$page.url.pathname}?tab=billing`,
disabled: !$isOrgAdmin
},
{
key: 4,
label: $t('settings.tabs.5'),
label: $t('settings.tabs.integrations_tab'),
tabKey: 'integrations',
href: `${$page.url.pathname}?tab=integrations`,
disabled: false
@@ -11,6 +11,7 @@
import TextChip from '$lib/components/Chip/Text.svelte';
import Plan from '$lib/components/Chip/Plan.svelte';
import { PLAN_NAMES } from 'shared/src/plans/constants';
import { t } from '$lib/utils/functions/translations';
export let canAddOrg = true;
@@ -90,7 +91,12 @@
/>
{/each}
<OrgSelectorItem disabled={true} size="" text=" + Add Organization" onClick={handleAddOrg} />
<OrgSelectorItem
disabled={true}
size=""
text={$t('navigation.add_organization')}
onClick={handleAddOrg}
/>
</Popover>
{/if}
</div>
@@ -49,7 +49,7 @@
});
if (!checkoutURL) {
snackbar.error('upgrade.generate_fail');
snackbar.error('snackbar.upgrade.generate_fail');
return;
}
@@ -57,7 +57,7 @@
} catch (error) {
console.error('Error subscribing', error);
snackbar.error('upgrade.failed');
snackbar.error('snackbar.upgrade.failed');
}
isLoadingPlan = null;
@@ -158,7 +158,7 @@
? 'text-white'
: 'text-gray-900'} dark:text-white lg:text-xl"
>
{$t(PLANS[planName].NAME)}
{PLANS[planName].NAME}
</p>
<p
class="text-baseline flex items-baseline gap-x-1 font-medium {planName ===
@@ -167,9 +167,9 @@
: 'text-black'} dark:text-gray-300"
>
{PLANS[planName].PRICE.CURRENCY}
{$t(PLANS[planName].PRICE.MONTHLY)}
{PLANS[planName].PRICE.MONTHLY}
{#if !PLANS[planName].PRICE.IS_PREMIUM}
/{$t('pricing.modal.month')}
/month
{/if}
</p>
<p
@@ -177,7 +177,7 @@
? 'text-white'
: 'text-black'} dark:text-gray-300 lg:leading-6"
>
{$t(PLANS[planName].DESCRIPTION)}
{PLANS[planName].DESCRIPTION}
</p>
<button
@@ -196,7 +196,7 @@
{#if isLoadingPlan === planName}
<Loading withOverlay={false} small />
{:else}
{$t(PLANS[planName].CTA.DASHBOARD_LABEL)}
{PLANS[planName].CTA.DASHBOARD_LABEL}
{/if}
</button>
@@ -215,7 +215,7 @@
/>
</div>
<p class="text-sm">
{$t(features)}
{features}
</p>
</li>
{/each}
@@ -34,9 +34,9 @@
<a href="https://app.classroomio.com/" class="text-primary-700 no-underline hover:no-underline"
>ClassroomIO</a
>
{$t('welcome_modal.content_1')}
<span class="text-primary-700">{$t('welcome_modal.content_2')};</span>
{$t('welcome_modal.content_3')}
{$t('welcome_modal.small_team')}
<span class="text-primary-700">{$t('welcome_modal.thank_you')};</span>
{$t('welcome_modal.deeply_appreciate')}
</p>
<img src="/images/welcome-img.svg" alt="A welcome banner" class="w-full my-6" />
</Modal>