i18n: add missing __() calls to complete French translation

This commit is contained in:
Youssef
2025-08-15 19:51:10 -04:00
parent cf5cc19fd5
commit 16afb24276
14 changed files with 210 additions and 27 deletions

View File

@@ -157,7 +157,7 @@
v-model="showHelpModal" v-model="showHelpModal"
v-model:articles="articles" v-model:articles="articles"
appName="learning" appName="learning"
title="Frappe Learning" :title="__('Frappe Learning')"
:logo="LMSLogo" :logo="LMSLogo"
:afterSkip="(step) => capture('onboarding_step_skipped_' + step)" :afterSkip="(step) => capture('onboarding_step_skipped_' + step)"
:afterSkipAll="() => capture('onboarding_steps_skipped')" :afterSkipAll="() => capture('onboarding_steps_skipped')"
@@ -303,7 +303,7 @@ const unreadNotifications = createResource({
const addNotifications = () => { const addNotifications = () => {
if (user) { if (user) {
sidebarLinks.value.push({ sidebarLinks.value.push({
label: 'Notifications', label: __('Notifications'),
icon: 'Bell', icon: 'Bell',
to: 'Notifications', to: 'Notifications',
activeFor: ['Notifications'], activeFor: ['Notifications'],
@@ -315,7 +315,7 @@ const addNotifications = () => {
const addQuizzes = () => { const addQuizzes = () => {
if (isInstructor.value || isModerator.value) { if (isInstructor.value || isModerator.value) {
sidebarLinks.value.splice(4, 0, { sidebarLinks.value.splice(4, 0, {
label: 'Quizzes', label: __('Quizzes'),
icon: 'CircleHelp', icon: 'CircleHelp',
to: 'Quizzes', to: 'Quizzes',
activeFor: [ activeFor: [
@@ -331,7 +331,7 @@ const addQuizzes = () => {
const addAssignments = () => { const addAssignments = () => {
if (isInstructor.value || isModerator.value) { if (isInstructor.value || isModerator.value) {
sidebarLinks.value.splice(5, 0, { sidebarLinks.value.splice(5, 0, {
label: 'Assignments', label: __('Assignments'),
icon: 'Pencil', icon: 'Pencil',
to: 'Assignments', to: 'Assignments',
activeFor: [ activeFor: [
@@ -347,7 +347,7 @@ const addAssignments = () => {
const addProgrammingExercises = () => { const addProgrammingExercises = () => {
if (isInstructor.value || isModerator.value) { if (isInstructor.value || isModerator.value) {
sidebarLinks.value.splice(3, 0, { sidebarLinks.value.splice(3, 0, {
label: 'Programming Exercises', label: __('Programming Exercises'),
icon: 'Code', icon: 'Code',
to: 'ProgrammingExercises', to: 'ProgrammingExercises',
activeFor: [ activeFor: [
@@ -383,7 +383,7 @@ const addPrograms = () => {
if (canAddProgram) { if (canAddProgram) {
sidebarLinks.value.splice(index, 0, { sidebarLinks.value.splice(index, 0, {
label: 'Programs', label: __('Programs'),
icon: 'Route', icon: 'Route',
to: 'Programs', to: 'Programs',
activeFor: activeFor, activeFor: activeFor,

View File

@@ -102,7 +102,7 @@ const props = defineProps({
}, },
emptyStateText: { emptyStateText: {
type: String, type: String,
default: 'Start a discussion', default: __('Start a discussion'),
}, },
singleThread: { singleThread: {
type: Boolean, type: Boolean,

View File

@@ -2,6 +2,7 @@
<div class="flex flex-col items-center justify-center mt-60"> <div class="flex flex-col items-center justify-center mt-60">
<GraduationCap class="size-10 mx-auto stroke-1 text-ink-gray-5" /> <GraduationCap class="size-10 mx-auto stroke-1 text-ink-gray-5" />
<div class="text-lg font-semibold text-ink-gray-7 mb-2.5"> <div class="text-lg font-semibold text-ink-gray-7 mb-2.5">
{{ __('No {0}').format(props.type) }} {{ __('No {0}').format(props.type) }}
</div> </div>
<div <div

View File

@@ -0,0 +1,70 @@
<template>
<div
v-if="!isSidebarCollapsed"
class="flex flex-col gap-3 shadow-sm rounded-lg py-2.5 px-3 bg-surface-modal text-base"
>
<div v-if="stepsCompleted != totalSteps" class="inline-flex text-ink-gray-9 gap-2">
<StepsIcon class="h-4 my-0.5 shrink-0" />
<div class="flex flex-col text-p-sm gap-0.5">
<div class="font-medium">
{{ __('Getting started') }}
</div>
<div class="text-ink-gray-7">
{{ __('{0}/{1} steps').format(stepsCompleted, totalSteps) }}
</div>
</div>
</div>
<div v-else class="flex flex-col gap-1">
<div class="flex items-center justify-between gap-1">
<div class="flex items-center gap-2 shrink-0">
<StepsIcon class="h-4 my-0.5" />
<div class="text-ink-gray-9 font-medium">
{{ __('You are all set') }}
</div>
</div>
<FeatherIcon
name="x"
class="h-4 cursor-pointer"
@click="() => { showHelpCenter = true; isOnboardingStepsCompleted = true }"
/>
</div>
<div class="text-p-sm text-ink-gray-7">
{{ __('All steps are completed successfully') }}
</div>
</div>
<Button
v-if="stepsCompleted != totalSteps"
:label="stepsCompleted == 0 ? __('Start now') : __('Continue')"
theme="blue"
@click="openOnboarding"
>
<template #prefix>
<FeatherIcon name="chevrons-right" class="size-4" />
</template>
</Button>
</div>
<Button v-else-if="stepsCompleted != totalSteps" @click="openOnboarding">
<StepsIcon class="h-4 my-0.5 shrink-0" />
</Button>
</template>
<script setup>
import StepsIcon from 'frappe-ui/frappe/Icons/StepsIcon.vue'
import Button from 'frappe-ui/src/components/Button/Button.vue'
import FeatherIcon from 'frappe-ui/src/components/FeatherIcon.vue'
import { useOnboarding } from 'frappe-ui/frappe/Onboarding/onboarding'
import { showHelpCenter } from 'frappe-ui/frappe/HelpCenter/helpCenter'
import { showHelpModal, minimize } from 'frappe-ui/frappe/Help/help'
const props = defineProps({
isSidebarCollapsed: { type: Boolean, default: false },
appName: { type: String, default: 'frappecrm' },
})
const { stepsCompleted, totalSteps, isOnboardingStepsCompleted } = useOnboarding(props.appName)
const openOnboarding = () => {
minimize.value = false
showHelpModal.value = true
}
</script>

View File

@@ -0,0 +1,112 @@
<template>
<div class="flex flex-col justify-center items-center gap-1 mt-4 mb-7">
<component :is="logo" class="size-10 shrink-0 rounded mb-4" />
<div class="text-base font-medium">
{{ __('Welcome to {0}').format(title) }}
</div>
<div class="text-p-base font-normal">
{{ __('{0}/{1} steps completed').format(stepsCompleted, totalSteps) }}
</div>
</div>
<div class="flex flex-col gap-2.5 overflow-hidden">
<div class="flex justify-between items-center py-0.5">
<Badge
:label="__('{0}% completed').format(completedPercentage)"
:theme="completedPercentage == 100 ? 'green' : 'orange'"
size="lg"
/>
<div class="flex">
<Button
v-if="completedPercentage != 0"
variant="ghost"
:label="__('Reset all')"
@click="() => resetAll(afterResetAll)"
/>
<Button
v-if="completedPercentage != 100"
variant="ghost"
:label="__('Skip all')"
@click="() => skipAll(afterSkipAll)"
/>
</div>
</div>
<div class="flex flex-col gap-1.5 overflow-y-auto">
<div
v-for="step in steps"
:key="step.title"
class="group w-full flex gap-2 justify-between items-center hover:bg-surface-gray-1 rounded px-2 py-1.5 cursor-pointer"
@click.stop="() => !step.completed && !isDependent(step) && step.onClick()"
>
<component :is="isDependent(step) ? Tooltip : 'div'" :text="dependsOnTooltip(step)">
<div
class="flex gap-2 items-center"
:class="[
step.completed
? 'text-ink-gray-5'
: isDependent(step)
? 'text-ink-gray-4'
: 'text-ink-gray-8',
]"
>
<component :is="step.icon" class="h-4" />
<div class="text-base" :class="{ 'line-through': step.completed }">
{{ step.title }}
</div>
</div>
</component>
<Button
v-if="!step.completed && !isDependent(step)"
:label="__('Skip')"
class="!h-4 text-xs !text-ink-gray-6 hidden group-hover:flex"
@click="() => skip(step.name, afterSkip)"
/>
<Button
v-else-if="!isDependent(step)"
:label="__('Reset')"
class="!h-4 text-xs !text-ink-gray-6 hidden group-hover:flex"
@click.stop="() => reset(step.name, afterReset)"
/>
</div>
</div>
</div>
</template>
<script setup>
import { useOnboarding } from 'frappe-ui/frappe/Onboarding/onboarding'
import Tooltip from 'frappe-ui/src/components/Tooltip/Tooltip.vue'
import Button from 'frappe-ui/src/components/Button/Button.vue'
import Badge from 'frappe-ui/src/components/Badge/Badge.vue'
const props = defineProps({
appName: { type: String, default: 'frappecrm' },
title: { type: String, default: 'Frappe CRM' },
logo: { type: Object, required: true },
afterSkip: { type: Function, default: () => {} },
afterSkipAll: { type: Function, default: () => {} },
afterReset: { type: Function, default: () => {} },
afterResetAll: { type: Function, default: () => {} },
})
function isDependent(step) {
if (step.dependsOn && !step.completed) {
const dependsOnStep = steps.find((s) => s.name === step.dependsOn)
if (dependsOnStep && !dependsOnStep.completed) {
return true
}
}
return false
}
function dependsOnTooltip(step) {
if (step.dependsOn && !step.completed) {
const dependsOnStep = steps.find((s) => s.name === step.dependsOn)
if (dependsOnStep && !dependsOnStep.completed) {
return `You need to complete "${dependsOnStep.title}" first.`
}
}
return ''
}
const { steps, stepsCompleted, totalSteps, completedPercentage, skip, skipAll, reset, resetAll } =
useOnboarding(props.appName)
</script>

View File

@@ -57,7 +57,7 @@
}" }"
> >
</ListView> </ListView>
<EmptyState v-else type="Assignments" /> <EmptyState v-else :type="__('Assignments').toLowerCase()" />
<div <div
v-if="assignments.data && assignments.hasNextPage" v-if="assignments.data && assignments.hasNextPage"
class="flex justify-center my-5" class="flex justify-center my-5"
@@ -198,7 +198,7 @@ const assignmentTypes = computed(() => {
const breadcrumbs = computed(() => [ const breadcrumbs = computed(() => [
{ {
label: 'Assignments', label: __('Assignments'),
route: { name: 'Assignments' }, route: { name: 'Assignments' },
}, },
]) ])

View File

@@ -70,7 +70,7 @@
<BatchCard :batch="batch" /> <BatchCard :batch="batch" />
</router-link> </router-link>
</div> </div>
<EmptyState v-else-if="!batches.list.loading" type="Batches" /> <EmptyState v-else-if="!batches.list.loading" :type="__('Batches').toLowerCase()" />
<div <div
v-if="!batches.list.loading && batches.hasNextPage" v-if="!batches.list.loading && batches.hasNextPage"

View File

@@ -81,7 +81,7 @@
</router-link> </router-link>
</div> </div>
</div> </div>
<EmptyState v-else type="Job Openings" /> <EmptyState v-else :type="__('Job Openings').toLowerCase()" />
</div> </div>
</div> </div>
</template> </template>

View File

@@ -214,7 +214,7 @@
<div class="mt-20" ref="discussionsContainer"> <div class="mt-20" ref="discussionsContainer">
<Discussions <Discussions
v-if="allowDiscussions" v-if="allowDiscussions"
:title="'Questions'" :title="__('Questions')"
:doctype="'Course Lesson'" :doctype="'Course Lesson'"
:docname="lesson.data.name" :docname="lesson.data.name"
:key="lesson.data.name" :key="lesson.data.name"

View File

@@ -144,7 +144,7 @@ onUnmounted(() => {
const breadcrumbs = computed(() => { const breadcrumbs = computed(() => {
let crumbs = [ let crumbs = [
{ {
label: 'Notifications', label: __('Notifications'),
route: { route: {
name: 'Notifications', name: 'Notifications',
}, },
@@ -155,7 +155,7 @@ const breadcrumbs = computed(() => {
usePageMeta(() => { usePageMeta(() => {
return { return {
title: 'Notifications', title: __('Notifications'),
icon: brand.favicon, icon: brand.favicon,
} }
}) })

View File

@@ -323,12 +323,12 @@ const saveProgram = () => {
const courseColumns = computed(() => { const courseColumns = computed(() => {
return [ return [
{ {
label: 'Title', label: __('Title'),
key: 'course_title', key: 'course_title',
width: 3, width: 3,
}, },
{ {
label: 'ID', label: __('ID'),
key: 'course', key: 'course',
width: 3, width: 3,
}, },
@@ -338,19 +338,19 @@ const courseColumns = computed(() => {
const memberColumns = computed(() => { const memberColumns = computed(() => {
return [ return [
{ {
label: 'Member', label: __('Member'),
key: 'member', key: 'member',
width: 3, width: 3,
align: 'left', align: 'left',
}, },
{ {
label: 'Full Name', label: __('Full Name'),
key: 'full_name', key: 'full_name',
width: 3, width: 3,
align: 'left', align: 'left',
}, },
{ {
label: 'Progress (%)', label: __('Progress (%)'),
key: 'progress', key: 'progress',
width: 3, width: 3,
align: 'right', align: 'right',
@@ -361,11 +361,11 @@ const memberColumns = computed(() => {
const breadbrumbs = computed(() => { const breadbrumbs = computed(() => {
return [ return [
{ {
label: 'Programs', label: __('Programs'),
route: { name: 'Programs' }, route: { name: 'Programs' },
}, },
{ {
label: props.programName === 'new' ? 'New Program' : props.programName, label: props.programName === 'new' ? __('New Program') : props.programName,
}, },
] ]
}) })

View File

@@ -82,7 +82,7 @@
</div> </div>
</div> </div>
</div> </div>
<EmptyState v-else type="Programs" /> <EmptyState v-else :type="__('Programs').toLowerCase()" />
<Dialog <Dialog
v-model="showDialog" v-model="showDialog"

View File

@@ -19,7 +19,7 @@
: __('No Quizzes') : __('No Quizzes')
}} }}
</div> </div>
<FormControl v-model="search" type="text" placeholder="Search"> <FormControl v-model="search" type="text" :placeholder="__('Search')">
<template #prefix> <template #prefix>
<FeatherIcon name="search" class="size-4 text-ink-gray-5" /> <FeatherIcon name="search" class="size-4 text-ink-gray-5" />
</template> </template>
@@ -88,7 +88,7 @@
</template> </template>
</ListSelectBanner> </ListSelectBanner>
</ListView> </ListView>
<EmptyState v-else type="Quizzes" /> <EmptyState v-else :type="__('Quizzes').toLowerCase()" />
<div v-if="quizzes.hasNextPage" class="flex justify-center my-5"> <div v-if="quizzes.hasNextPage" class="flex justify-center my-5">
<Button @click="quizzes.next()"> <Button @click="quizzes.next()">
{{ __('Load More') }} {{ __('Load More') }}

View File

@@ -16,14 +16,14 @@
<Tooltip :text="__('Active Members')"> <Tooltip :text="__('Active Members')">
<NumberChart <NumberChart
class="border rounded-md" class="border rounded-md"
:config="{ title: 'Signups', value: chartDetails.data.users }" :config="{ title: __('Signups'), value: chartDetails.data.users }"
/> />
</Tooltip> </Tooltip>
<Tooltip :text="__('Course Enrollments')"> <Tooltip :text="__('Course Enrollments')">
<NumberChart <NumberChart
class="border rounded-md" class="border rounded-md"
:config="{ :config="{
title: 'Enrollments', title: __('Enrollments'),
value: chartDetails.data.enrollments, value: chartDetails.data.enrollments,
}" }"
/> />
@@ -122,7 +122,7 @@
:config="{ :config="{
data: courseCompletion.data, data: courseCompletion.data,
title: __('Completions'), title: __('Completions'),
subtitle: 'Course Completion', subtitle: __('Course Completion'),
categoryColumn: 'label', categoryColumn: 'label',
valueColumn: 'value', valueColumn: 'value',
}" }"