From 4a11ebdc22b2c572a283e985b2583badab2ce38d Mon Sep 17 00:00:00 2001 From: gakshita Date: Fri, 31 Jan 2025 13:40:19 +0530 Subject: [PATCH 1/2] feat: added language support for cycles --- web/core/components/issues/issue-detail/subscription.tsx | 2 +- web/core/components/project/search-projects.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/core/components/issues/issue-detail/subscription.tsx b/web/core/components/issues/issue-detail/subscription.tsx index a895c34c900..e19e787f398 100644 --- a/web/core/components/issues/issue-detail/subscription.tsx +++ b/web/core/components/issues/issue-detail/subscription.tsx @@ -5,9 +5,9 @@ import isNil from "lodash/isNil"; import { observer } from "mobx-react"; import { Bell, BellOff } from "lucide-react"; // plane-i18n +import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; // UI -import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { Button, Loader, TOAST_TYPE, setToast } from "@plane/ui"; // hooks import { useIssueDetail, useUserPermissions } from "@/hooks/store"; diff --git a/web/core/components/project/search-projects.tsx b/web/core/components/project/search-projects.tsx index 8c0dcbc9385..67e4a3ebb31 100644 --- a/web/core/components/project/search-projects.tsx +++ b/web/core/components/project/search-projects.tsx @@ -59,7 +59,7 @@ export const ProjectSearch: FC = observer(() => { updateSearchQuery(e.target.value)} onKeyDown={handleInputKeyDown} From 9718897c1ef569255b862745001aaeee5bd82964 Mon Sep 17 00:00:00 2001 From: gakshita Date: Fri, 31 Jan 2025 13:40:40 +0530 Subject: [PATCH 2/2] feat: added language support for cycles --- .../i18n/src/locales/en/translations.json | 75 +++++++++++++++++++ .../[projectId]/cycles/(list)/header.tsx | 12 ++- .../[projectId]/cycles/(list)/page.tsx | 2 +- .../components/cycles/active-cycle/root.tsx | 2 +- .../cycles/analytics-sidebar/base.tsx | 8 +- .../cycles/active-cycle/cycle-stats.tsx | 8 +- .../cycles/active-cycle/productivity.tsx | 8 +- .../cycles/active-cycle/progress.tsx | 2 +- .../analytics-sidebar/issue-progress.tsx | 10 ++- .../analytics-sidebar/progress-stats.tsx | 22 +++--- .../analytics-sidebar/sidebar-details.tsx | 20 ++--- .../analytics-sidebar/sidebar-header.tsx | 35 +++++---- .../components/cycles/cycles-view-header.tsx | 4 +- web/core/components/cycles/cycles-view.tsx | 8 +- web/core/components/cycles/form.tsx | 24 ++++-- .../cycles/list/cycle-list-item-action.tsx | 35 ++++----- web/core/components/cycles/list/root.tsx | 6 +- web/core/components/cycles/quick-actions.tsx | 28 +++---- 18 files changed, 213 insertions(+), 96 deletions(-) diff --git a/packages/i18n/src/locales/en/translations.json b/packages/i18n/src/locales/en/translations.json index ddd364e0e77..16a6bdeb9b5 100644 --- a/packages/i18n/src/locales/en/translations.json +++ b/packages/i18n/src/locales/en/translations.json @@ -1484,6 +1484,32 @@ }, "project_cycles": { + "add_cycle": "Add cycle", + "more_details": "More details", + "cycle": "Cycle", + "update_cycle": "Update cycle", + "create_cycle": "Create cycle", + "no_matching_cycles": "No matching cycles", + "remove_filters_to_see_all_cycles": "Remove the filters to see all cycles", + "remove_search_criteria_to_see_all_cycles": "Remove the search criteria to see all cycles", + "only_completed_cycles_can_be_archived": "Only completed cycles can be archived", + "active_cycle": { + "label" :"Active cycle", + "progress": "Progress", + "chart": "Burndown chart", + "priority_issue": "Priority issues", + "assignees": "Assignees", + "issue_burndown": "Issue burndown", + "ideal": "Ideal", + "current": "Current", + "labels": "Labels" + }, + "upcoming_cycle": { + "label": "Upcoming cycle" + }, + "completed_cycle": { + "label": "Completed cycle" + }, "status": { "days_left": "Days left", "completed": "Completed", @@ -1491,6 +1517,55 @@ "in_progress": "In progress", "draft": "Draft" }, + "action": { + "restore": { + "title": "Restore cycle", + "success": { + "title": "Cycle restored", + "description": "The cycle has been restored." + }, + "failed": { + "title": "Cycle restore failed", + "description": "The cycle could not be restored. Please try again." + } + }, + "favorite": { + "loading": "Adding cycle to favorites...", + "success": { + "description": "Cycle added to favorites.", + "title": "Success!" + }, + "failed": { + "description": "Couldn't add the cycle to favorites. Please try again.", + "title": "Error!" + } + }, + "unfavorite": { + "loading": "Removing cycle from favorites...", + "success": { + "description": "Cycle removed from favorites.", + "title": "Success!" + }, + "failed": { + "description": "Couldn't remove the cycle from favorites. Please try again.", + "title": "Error!" + } + }, + "update": { + "loading": "Updating cycle...", + "success": { + "description": "Cycle updated successfully.", + "title": "Success!" + }, + "failed": { + "description": "Error updating the cycle. Please try again.", + "title": "Error!" + }, + "error": { + "already_exists": "You already have a cycle on the given dates, if you want to create a draft cycle, you can do that by removing both the dates." + } + } + }, "empty_state": { "general": { "title": "Group and timebox your work in Cycles.", diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/header.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/header.tsx index b067c873298..28a99a2b966 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/header.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/header.tsx @@ -4,6 +4,7 @@ import { FC } from "react"; import { observer } from "mobx-react"; // ui import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; +import { useTranslation } from "@plane/i18n"; import { Breadcrumbs, Button, ContrastIcon, Header } from "@plane/ui"; // components import { BreadcrumbLink } from "@/components/common"; @@ -23,6 +24,7 @@ export const CyclesListHeader: FC = observer(() => { const { setTrackElement } = useEventTracker(); const { allowPermissions } = useUserPermissions(); const { currentProjectDetails, loader } = useProject(); + const { t } = useTranslation(); const canUserCreateCycle = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], @@ -36,7 +38,12 @@ export const CyclesListHeader: FC = observer(() => { } />} + link={ + } + /> + } /> @@ -51,7 +58,8 @@ export const CyclesListHeader: FC = observer(() => { toggleCreateCycleModal(true); }} > -
Add
Cycle +
{t("add")}
+
{t("add_cycle")}
) : ( diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx index b72b46dbeaf..d6b4eea15bc 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/page.tsx @@ -38,7 +38,7 @@ const ProjectCyclesPage = observer(() => { // derived values const totalCycles = currentProjectCycleIds?.length ?? 0; const project = projectId ? getProjectById(projectId?.toString()) : undefined; - const pageTitle = project?.name ? `${project?.name} - Cycles` : undefined; + const pageTitle = project?.name ? `${project?.name} - ${t("cycles.label", { count: 2 })}` : undefined; const hasAdminLevelPermission = allowPermissions([EUserProjectRoles.ADMIN], EUserPermissionsLevel.PROJECT); const hasMemberLevelPermission = allowPermissions( [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER], diff --git a/web/ce/components/cycles/active-cycle/root.tsx b/web/ce/components/cycles/active-cycle/root.tsx index a13b7f64cfa..6b2ecb928be 100644 --- a/web/ce/components/cycles/active-cycle/root.tsx +++ b/web/ce/components/cycles/active-cycle/root.tsx @@ -97,7 +97,7 @@ export const ActiveCycleRoot: React.FC = observer((props) = {({ open }) => ( <> - + {ActiveCyclesComponent} diff --git a/web/ce/components/cycles/analytics-sidebar/base.tsx b/web/ce/components/cycles/analytics-sidebar/base.tsx index 87f07e387c0..c259a4a81d2 100644 --- a/web/ce/components/cycles/analytics-sidebar/base.tsx +++ b/web/ce/components/cycles/analytics-sidebar/base.tsx @@ -2,6 +2,7 @@ import { FC, Fragment } from "react"; import { observer } from "mobx-react"; // plane ui +import { useTranslation } from "@plane/i18n"; import { TCycleEstimateType } from "@plane/types"; import { Loader } from "@plane/ui"; // components @@ -23,6 +24,7 @@ export const SidebarChart: FC = observer((props) => { // hooks const { getEstimateTypeByCycleId, getCycleById, fetchCycleDetails, fetchArchivedCycleDetails, setEstimateType } = useCycle(); + const { t } = useTranslation(); // derived data const cycleDetails = validateCycleSnapshot(getCycleById(cycleId)); @@ -66,11 +68,11 @@ export const SidebarChart: FC = observer((props) => {
- Ideal + {t("ideal")}
- Current + {t("current")}
{cycleStartDate && cycleEndDate && completionChartDistributionData ? ( @@ -80,7 +82,7 @@ export const SidebarChart: FC = observer((props) => { startDate={cycleStartDate} endDate={cycleEndDate} totalIssues={estimateType === "points" ? totalEstimatePoints : totalIssues} - plotTitle={estimateType === "points" ? "points" : "issues"} + plotTitle={estimateType === "points" ? t("points") : t("work_items")} /> ) : ( diff --git a/web/core/components/cycles/active-cycle/cycle-stats.tsx b/web/core/components/cycles/active-cycle/cycle-stats.tsx index b9cf267a62f..79343d65c30 100644 --- a/web/core/components/cycles/active-cycle/cycle-stats.tsx +++ b/web/core/components/cycles/active-cycle/cycle-stats.tsx @@ -126,7 +126,7 @@ export const ActiveCycleStats: FC = observer((props) => { ) } > - Priority Issues + {t("project_cycles.active_cycle.priority_issue")} @@ -139,7 +139,7 @@ export const ActiveCycleStats: FC = observer((props) => { ) } > - Assignees + {t("project_cycles.active_cycle.assignees")} @@ -152,7 +152,7 @@ export const ActiveCycleStats: FC = observer((props) => { ) } > - Labels + {t("project_cycles.active_cycle.labels")} @@ -289,7 +289,7 @@ export const ActiveCycleStats: FC = observer((props) => {
User
- No assignee + {t("no_assignee")} } completed={assignee.completed_issues} diff --git a/web/core/components/cycles/active-cycle/productivity.tsx b/web/core/components/cycles/active-cycle/productivity.tsx index 859c3015435..6629e527771 100644 --- a/web/core/components/cycles/active-cycle/productivity.tsx +++ b/web/core/components/cycles/active-cycle/productivity.tsx @@ -43,7 +43,9 @@ export const ActiveCycleProductivity: FC = observe
-

Issue burndown

+

+ {t("project_cycles.active_cycle.issue_burndown")} +

@@ -56,11 +58,11 @@ export const ActiveCycleProductivity: FC = observe
- Ideal + {t("project_cycles.active_cycle.ideal")}
- Current + {t("project_cycles.active_cycle.current")}
{estimateType === "points" ? ( diff --git a/web/core/components/cycles/active-cycle/progress.tsx b/web/core/components/cycles/active-cycle/progress.tsx index 0f2c5a419aa..65bac5a7772 100644 --- a/web/core/components/cycles/active-cycle/progress.tsx +++ b/web/core/components/cycles/active-cycle/progress.tsx @@ -47,7 +47,7 @@ export const ActiveCycleProgress: FC = observer((props
-

Progress

+

{t("project_cycles.active_cycle.progress")}

{cycle.total_issues > 0 && ( {`${cycle.completed_issues + cycle.cancelled_issues}/${cycle.total_issues - cycle.cancelled_issues} ${ diff --git a/web/core/components/cycles/analytics-sidebar/issue-progress.tsx b/web/core/components/cycles/analytics-sidebar/issue-progress.tsx index 0efdaded511..eb75e1bcea4 100644 --- a/web/core/components/cycles/analytics-sidebar/issue-progress.tsx +++ b/web/core/components/cycles/analytics-sidebar/issue-progress.tsx @@ -8,6 +8,7 @@ import { useSearchParams } from "next/navigation"; import { ChevronUp, ChevronDown } from "lucide-react"; import { Disclosure, Transition } from "@headlessui/react"; import { EIssueFilterType, EIssuesStoreType } from "@plane/constants"; +import { useTranslation } from "@plane/i18n"; import { ICycle, IIssueFilterOptions, TCyclePlotType, TProgressSnapshot } from "@plane/types"; // components import { CycleProgressStats } from "@/components/cycles"; @@ -63,6 +64,7 @@ export const CycleAnalyticsProgress: FC = observer((pro const { issuesFilter: { issueFilters, updateFilters }, } = useIssues(EIssuesStoreType.CYCLE); + const { t } = useTranslation(); // derived values const cycleDetails = validateCycleSnapshot(getCycleById(cycleId)); @@ -138,7 +140,9 @@ export const CycleAnalyticsProgress: FC = observer((pro {isCycleDateValid ? (
-
Progress
+
+ {t("project_cycles.active_cycle.progress")} +
{open ? ( @@ -150,7 +154,9 @@ export const CycleAnalyticsProgress: FC = observer((pro
) : (
-
Progress
+
+ {t("project_cycles.active_cycle.progress")} +
)} diff --git a/web/core/components/cycles/analytics-sidebar/progress-stats.tsx b/web/core/components/cycles/analytics-sidebar/progress-stats.tsx index 069ccb2fff5..8336430ccab 100644 --- a/web/core/components/cycles/analytics-sidebar/progress-stats.tsx +++ b/web/core/components/cycles/analytics-sidebar/progress-stats.tsx @@ -4,6 +4,7 @@ import { FC } from "react"; import { observer } from "mobx-react"; import Image from "next/image"; import { Tab } from "@headlessui/react"; +import { useTranslation } from "@plane/i18n"; import { IIssueFilterOptions, IIssueFilters, @@ -73,6 +74,7 @@ type TStateStatComponent = { export const AssigneeStatComponent = observer((props: TAssigneeStatComponent) => { const { distribution, isEditable, filters, handleFiltersUpdate } = props; + const { t } = useTranslation(); return (
{distribution && distribution.length > 0 ? ( @@ -104,7 +106,7 @@ export const AssigneeStatComponent = observer((props: TAssigneeStatComponent) =>
User
- No assignee + {t("no_assignee")}
} completed={assignee?.completed ?? 0} @@ -117,7 +119,7 @@ export const AssigneeStatComponent = observer((props: TAssigneeStatComponent) =>
empty members
-
No assignees yet
+
{t("no_assignee")}
)}
@@ -126,6 +128,7 @@ export const AssigneeStatComponent = observer((props: TAssigneeStatComponent) => export const LabelStatComponent = observer((props: TLabelStatComponent) => { const { distribution, isEditable, filters, handleFiltersUpdate } = props; + const { t } = useTranslation(); return (
{distribution && distribution.length > 0 ? ( @@ -142,7 +145,7 @@ export const LabelStatComponent = observer((props: TLabelStatComponent) => { backgroundColor: label.color ?? "transparent", }} /> - {label.title ?? "No labels"} + {label.title ?? t("no_labels_yet")}
} completed={label.completed} @@ -165,7 +168,7 @@ export const LabelStatComponent = observer((props: TLabelStatComponent) => { backgroundColor: label.color ?? "transparent", }} /> - {label.title ?? "No labels"} + {label.title ?? t("no_labels_yet")}
} completed={label.completed} @@ -179,7 +182,7 @@ export const LabelStatComponent = observer((props: TLabelStatComponent) => {
empty label
-
No labels yet
+
{t("no_labels_yet")}
)} @@ -222,15 +225,15 @@ export const StateStatComponent = observer((props: TStateStatComponent) => { const progressStats = [ { key: "stat-states", - title: "States", + i18n_title: "common.states", }, { key: "stat-assignees", - title: "Assignees", + i18n_title: "common.assignees", }, { key: "stat-labels", - title: "Labels", + i18n_title: "common.labels", }, ]; @@ -267,6 +270,7 @@ export const CycleProgressStats: FC = observer((props) => { `cycle-analytics-tab-${cycleId}`, "stat-assignees" ); + const { t } = useTranslation(); // derived values const currentTabIndex = (tab: string): number => progressStats.findIndex((stat) => stat.key === tab); @@ -337,7 +341,7 @@ export const CycleProgressStats: FC = observer((props) => { key={stat.key} onClick={() => setCycleTab(stat.key)} > - {stat.title} + {t(stat.i18n_title)} ))} diff --git a/web/core/components/cycles/analytics-sidebar/sidebar-details.tsx b/web/core/components/cycles/analytics-sidebar/sidebar-details.tsx index c60b5dae95c..eaa460f7abe 100644 --- a/web/core/components/cycles/analytics-sidebar/sidebar-details.tsx +++ b/web/core/components/cycles/analytics-sidebar/sidebar-details.tsx @@ -4,6 +4,7 @@ import isEmpty from "lodash/isEmpty"; import { observer } from "mobx-react"; import { LayersIcon, SquareUser, Users } from "lucide-react"; // plane types +import { useTranslation } from "@plane/i18n"; import { ICycle } from "@plane/types"; // plane ui import { Avatar, AvatarGroup, TextArea } from "@plane/ui"; @@ -24,6 +25,7 @@ export const CycleSidebarDetails: FC = observer((props) => { // hooks const { getUserDetails } = useMember(); const { areEstimateEnabledByProjectId, currentActiveEstimateId, estimateById } = useProjectEstimates(); + const { t } = useTranslation(); const areEstimateEnabled = projectId && areEstimateEnabledByProjectId(projectId.toString()); const cycleStatus = cycleDetails?.status?.toLocaleLowerCase(); @@ -32,10 +34,10 @@ export const CycleSidebarDetails: FC = observer((props) => { const issueCount = isCompleted && !isEmpty(cycleDetails?.progress_snapshot) ? cycleDetails?.progress_snapshot?.total_issues === 0 - ? "0 Issue" + ? `0 ${t("common.work_item")}` : `${cycleDetails?.progress_snapshot?.completed_issues}/${cycleDetails?.progress_snapshot?.total_issues}` : cycleDetails?.total_issues === 0 - ? "0 Issue" + ? `0 ${t("common.work_item")}` : `${cycleDetails?.completed_issues}/${cycleDetails?.total_issues}`; const estimateType = areEstimateEnabled && currentActiveEstimateId && estimateById(currentActiveEstimateId); const cycleOwnerDetails = cycleDetails ? getUserDetails(cycleDetails.owned_by_id) : undefined; @@ -51,10 +53,10 @@ export const CycleSidebarDetails: FC = observer((props) => { const issueEstimatePointCount = isCompleted && !isEmpty(cycleDetails?.progress_snapshot) ? cycleDetails?.progress_snapshot.total_issues === 0 - ? "0 Issue" + ? `0 ${t("common.work_item")}` : `${cycleDetails?.progress_snapshot.completed_estimate_points}/${cycleDetails?.progress_snapshot.total_estimate_points}` : cycleDetails?.total_issues === 0 - ? "0 Issue" + ? `0 ${t("common.work_item")}` : `${cycleDetails?.completed_estimate_points}/${cycleDetails?.total_estimate_points}`; return (
@@ -70,7 +72,7 @@ export const CycleSidebarDetails: FC = observer((props) => {
- Lead + {t("lead")}
@@ -83,7 +85,7 @@ export const CycleSidebarDetails: FC = observer((props) => {
- Members + {t("members")}
@@ -104,7 +106,7 @@ export const CycleSidebarDetails: FC = observer((props) => { ) : ( - No assignees + {t("no_assignee")} )}
@@ -113,7 +115,7 @@ export const CycleSidebarDetails: FC = observer((props) => {
- Issues + {t("work_items")}
{issueCount} @@ -127,7 +129,7 @@ export const CycleSidebarDetails: FC = observer((props) => {
- Points + {t("points")}
{issueEstimatePointCount} diff --git a/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx b/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx index 2c4037d7db9..c8d4cc487b4 100644 --- a/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx +++ b/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx @@ -5,7 +5,7 @@ import { observer } from "mobx-react"; import { Controller, useForm } from "react-hook-form"; import { ArchiveIcon, ArchiveRestoreIcon, ChevronRight, EllipsisIcon, LinkIcon, Trash2 } from "lucide-react"; // types -import { CYCLE_STATUS, CYCLE_UPDATED ,EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; +import { CYCLE_STATUS, CYCLE_UPDATED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; import { ICycle } from "@plane/types"; // ui @@ -70,16 +70,16 @@ export const CycleSidebarHeader: FC = observer((props) => { .then(() => { setToast({ type: TOAST_TYPE.SUCCESS, - title: "Restore success", - message: "Your cycle can be found in project cycles.", + title: t("project_cycles.action.restore.success.title"), + message: t("project_cycles.action.restore.success.description"), }); router.push(`/${workspaceSlug.toString()}/projects/${projectId.toString()}/archives/cycles`); }) .catch(() => setToast({ type: TOAST_TYPE.ERROR, - title: "Error!", - message: "Cycle could not be restored. Please try again.", + title: t("project_cycles.action.restore.failed.title"), + message: t("project_cycles.action.restore.failed.description"), }) ); }; @@ -89,14 +89,14 @@ export const CycleSidebarHeader: FC = observer((props) => { .then(() => { setToast({ type: TOAST_TYPE.SUCCESS, - title: "Link Copied!", - message: "Cycle link copied to clipboard.", + title: t("common.link_copied"), + message: t("common.link_copied_to_clipboard"), }); }) .catch(() => { setToast({ type: TOAST_TYPE.ERROR, - title: "Some error occurred", + title: t("common.errors.default.message"), }); }); }; @@ -166,15 +166,14 @@ export const CycleSidebarHeader: FC = observer((props) => { submitChanges(payload, "date_range"); setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success!", - message: "Cycle updated successfully.", + title: t("project_cycles.action.update.success.title"), + message: t("project_cycles.action.update.success.description"), }); } else { setToast({ type: TOAST_TYPE.ERROR, - title: "Error!", - message: - "You already have a cycle on the given dates, if you want to create a draft cycle, you can do that by removing both the dates.", + title: t("project_cycles.action.update.failed.title"), + message: t("project_cycles.action.update.error.already_exists"), }); reset({ ...cycleDetails }); } @@ -231,15 +230,15 @@ export const CycleSidebarHeader: FC = observer((props) => { {isCompleted ? (
- Archive cycle + {t("common.archive")}
) : (
-

Archive cycle

+

{t("common.archive")}

- Only completed cycles
can be archived. + {t("project_cycles.only_completed_cycles_can_be_archived")}

@@ -250,7 +249,7 @@ export const CycleSidebarHeader: FC = observer((props) => { - Restore cycle + {t("project_cycles.action.restore.title")} )} @@ -263,7 +262,7 @@ export const CycleSidebarHeader: FC = observer((props) => { > - Delete cycle + {t("delete")} )} diff --git a/web/core/components/cycles/cycles-view-header.tsx b/web/core/components/cycles/cycles-view-header.tsx index c3139bc8732..fe5b942dbf2 100644 --- a/web/core/components/cycles/cycles-view-header.tsx +++ b/web/core/components/cycles/cycles-view-header.tsx @@ -5,6 +5,7 @@ import { ListFilter, Search, X } from "lucide-react"; // plane helpers import { useOutsideClickDetector } from "@plane/hooks"; // types +import { useTranslation } from "@plane/i18n"; import { TCycleFilters } from "@plane/types"; // components import { CycleFiltersSelection } from "@/components/cycles"; @@ -25,6 +26,7 @@ export const CyclesViewHeader: React.FC = observer((props) => { const inputRef = useRef(null); // hooks const { currentProjectFilters, searchQuery, updateFilters, updateSearchQuery } = useCycleFilter(); + const { t } = useTranslation(); // states const [isSearchOpen, setIsSearchOpen] = useState(searchQuery !== "" ? true : false); // outside click detector hook @@ -114,7 +116,7 @@ export const CyclesViewHeader: React.FC = observer((props) => {
} - title="Filters" + title={t("common.filters")} placement="bottom-end" isFiltersApplied={isFiltersApplied} > diff --git a/web/core/components/cycles/cycles-view.tsx b/web/core/components/cycles/cycles-view.tsx index dea4554129f..ef6c9d136a2 100644 --- a/web/core/components/cycles/cycles-view.tsx +++ b/web/core/components/cycles/cycles-view.tsx @@ -2,6 +2,7 @@ import { FC } from "react"; import { observer } from "mobx-react"; import Image from "next/image"; // components +import { useTranslation } from "@plane/i18n"; import { CyclesList } from "@/components/cycles"; // ui import { CycleModuleListLayout } from "@/components/ui"; @@ -21,6 +22,7 @@ export const CyclesView: FC = observer((props) => { // store hooks const { getFilteredCycleIds, getFilteredCompletedCycleIds, loader, currentProjectActiveCycleId } = useCycle(); const { searchQuery } = useCycleFilter(); + const { t } = useTranslation(); // derived values const filteredCycleIds = getFilteredCycleIds(projectId, false); const filteredCompletedCycleIds = getFilteredCompletedCycleIds(projectId); @@ -39,11 +41,11 @@ export const CyclesView: FC = observer((props) => { className="mx-auto h-36 w-36 sm:h-48 sm:w-48" alt="No matching cycles" /> -
No matching cycles
+
{t("project_cycles.no_matching_cycles")}

{searchQuery.trim() === "" - ? "Remove the filters to see all cycles" - : "Remove the search criteria to see all cycles"} + ? t("project_cycles.remove_filters_to_see_all_cycles") + : t("project_cycles.remove_search_criteria_to_see_all_cycles")}

diff --git a/web/core/components/cycles/form.tsx b/web/core/components/cycles/form.tsx index e11cbef1355..c5c9c918697 100644 --- a/web/core/components/cycles/form.tsx +++ b/web/core/components/cycles/form.tsx @@ -5,6 +5,7 @@ import { Controller, useForm } from "react-hook-form"; // plane imports import { ETabIndices } from "@plane/constants"; // types +import { useTranslation } from "@plane/i18n"; import { ICycle } from "@plane/types"; // ui import { Button, Input, TextArea } from "@plane/ui"; @@ -35,6 +36,7 @@ const defaultValues: Partial = { export const CycleForm: React.FC = (props) => { const { handleFormSubmit, handleClose, status, projectId, setActiveProject, data, isMobile = false } = props; + const { t } = useTranslation(); // form data const { formState: { errors, isSubmitting, dirtyFields }, @@ -85,7 +87,9 @@ export const CycleForm: React.FC = (props) => { )} /> )} -

{status ? "Update" : "Create"} cycle

+

+ {status ? t("project_cycles.update_cycle") : t("project_cycles.create_cycle")} +

@@ -93,17 +97,17 @@ export const CycleForm: React.FC = (props) => { name="name" control={control} rules={{ - required: "Title is required", + required: t("title_is_required"), maxLength: { value: 255, - message: "Title should be less than 255 characters", + message: t("title_should_be_less_than_255_characters"), }, }} render={({ field: { value, onChange } }) => ( = (props) => { render={({ field: { value, onChange } }) => (