From f4ffdf16dfbb2c35475782435b90359c5d133c65 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Wed, 29 Jan 2025 14:53:06 +0530 Subject: [PATCH 1/2] chore: project settings language support --- .../i18n/src/locales/en/translations.json | 95 ++++++++++++++++++- .../[projectId]/settings/automations/page.tsx | 5 +- .../(detail)/[projectId]/settings/header.tsx | 5 +- .../(detail)/[projectId]/settings/sidebar.tsx | 5 +- .../[projectId]/settings/states/page.tsx | 5 +- web/ce/constants/project/settings/tabs.ts | 18 ++-- .../automation/auto-archive-automation.tsx | 10 +- .../automation/auto-close-automation.tsx | 16 ++-- .../components/estimates/empty-screen.tsx | 13 ++- web/core/components/estimates/root.tsx | 11 ++- .../labels/create-update-label-inline.tsx | 17 ++-- .../labels/project-setting-label-list.tsx | 2 +- web/core/components/project/form.tsx | 48 +++++----- web/core/components/project/member-list.tsx | 10 +- .../project-settings-member-defaults.tsx | 21 ++-- .../project/send-project-invitation-modal.tsx | 17 ++-- 16 files changed, 211 insertions(+), 87 deletions(-) diff --git a/packages/i18n/src/locales/en/translations.json b/packages/i18n/src/locales/en/translations.json index cb4bf290050..a631b3798d7 100644 --- a/packages/i18n/src/locales/en/translations.json +++ b/packages/i18n/src/locales/en/translations.json @@ -14,8 +14,11 @@ "description": "Description", "search": "Search", "add_member": "Add member", + "adding_members": "Adding members", + "no_matching_members": "No matching members", "remove_member": "Remove member", "add_members": "Add members", + "adding_member": "Adding members", "remove_members": "Remove members", "add": "Add", "adding": "Adding", @@ -197,6 +200,10 @@ "description_placeholder": "Description...", "only_alphanumeric_non_latin_characters_allowed": "Only Alphanumeric & Non-latin characters are allowed.", "project_id_is_required": "Project ID is required", + "project_id_allowed_char": "Only Alphanumeric & Non-latin characters are allowed.", + "project_id_min_char": "Project ID must at least be of 1 character", + "project_id_max_char": "Project ID must at most be of 5 characters", + "project_description_placeholder": "Enter project description", "select_network": "Select network", "lead": "Lead", "date_range": "Date range", @@ -504,6 +511,21 @@ "description": "Description", "title": "Title", "attachment": "Attachment", + "general": "General", + "features": "Features", + "automation": "Automation", + "project_name": "Project name", + "project_id": "Project ID", + "project_timezone": "Project Timezone", + "created_on": "Created on", + "update_project": "Update project", + "identifier_already_exists": "Identifier already exists", + "add_more": "Add more", + "defaults": "Defaults", + "add_label": "Add label", + "something_went_wrong": "Something went wrong. Please try again later.", + "estimates": "estimates", + "customize_time_range": "Customize time range", "access": { "public": "Public", "private": "Private" @@ -685,7 +707,7 @@ "total_members": "Total members", "total_cycles": "Total cycles", "total_modules": "Total modules", - "pending_work_items":{ + "pending_work_items": { "title": "Pending work items", "empty_state": "Analysis of pending work items by co-workers appears here." }, @@ -693,11 +715,11 @@ "title": "Work items closed in a year", "empty_state": "Close work items to view analysis of the same in the form of a graph." }, - "most_work_items_created":{ + "most_work_items_created": { "title": "Most work items created", "empty_state": "Co-workers and the number of work items created by them appears here." }, - "most_work_items_closed":{ + "most_work_items_closed": { "title": "Most work items closed", "empty_state": "Co-workers and the number of work items closed by them appears here." }, @@ -933,10 +955,77 @@ }, "project_settings": { + "general": { + "enter_project_id": "Enter project ID", + "please_select_a_timezone": "Please select a timezone", + "archive_project": { + "title": "Archive project", + "description": "Archiving a project will unlist your project from your side navigation although you will still be able to access it from your projects page. You can restore the project or delete it whenever you want.", + "button": "Archive project" + }, + "delete_project": { + "title": "Delete project", + "description": "When deleting a project, all of the data and resources within that project will be permanently removed and cannot be recovered.", + "button": "Delete my project" + }, + "toast": { + "success": "Project updated successfully", + "error": "Project could not be updated. Please try again." + } + }, + "members": { + "label": "Members", + "project_lead": "Project lead", + "default_assignee": "Default assignee", + "guest_super_permissions": { + "title": "Grant view access to all issues for guest users:", + "sub_heading": "This will allow guests to have view access to all the project issues." + }, + "invite_members": { + "title": "Invite members", + "sub_heading": "Invite members to work on your project.", + "select_co_worker": "Select co-worker" + } + }, + "states": { + "describe_this_state_for_your_members": "Describe this state for your members." + }, + "labels": { + "label_title": "Label title", + "label_title_is_required": "Label title is required", + "label_max_char": "Label name should not exceed 255 characters", + "toast": { + "error": "Error while updating the label" + } + }, + "estimates": { + "title": "Enable estimates for my project", + "description": "They help you in communicating complexity and workload of the team." + }, + "automations": { + "label": "Automations", + "auto-archive": { + "title": "Auto-archive closed issues", + "description": "Plane will auto archive issues that have been completed or canceled.", + "duration": "Auto-archive issues that are closed for" + }, + "auto-close": { + "title": "Auto-close issues", + "description": "Plane will automatically close issues that haven't been completed or canceled.", + "duration": "Auto-close issues that are inactive for", + "auto_close_status": "Auto-close status" + } + }, + "empty_state": { "labels": { "title": "No labels yet", "description": "Create labels to help organize and filter issues in you project." + }, + "estimates": { + "title": "No estimate systems yet", + "description": "Create a set of estimates to communicate the amount of work per issue.", + "primary_button": "Add estimate system" } } }, diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/automations/page.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/automations/page.tsx index 141cc39feda..5cc086d3d38 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/automations/page.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/automations/page.tsx @@ -5,6 +5,7 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { IProject } from "@plane/types"; // ui +import { useTranslation } from "@plane/i18n"; import { TOAST_TYPE, setToast } from "@plane/ui"; // components import { NotAuthorizedView } from "@/components/auth-screens"; @@ -21,6 +22,8 @@ const AutomationSettingsPage = observer(() => { const { workspaceUserInfo, allowPermissions } = useUserPermissions(); const { currentProjectDetails: projectDetails, updateProject } = useProject(); + const { t } = useTranslation(); + // derived values const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT); @@ -48,7 +51,7 @@ const AutomationSettingsPage = observer(() => {
-

Automations

+

{t("project_settings.automations.label")}

diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/header.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/header.tsx index 2dfd24ea051..608a9888e1a 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/header.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/header.tsx @@ -5,6 +5,7 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // ui import { Settings } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; import { Breadcrumbs, CustomMenu, Header } from "@plane/ui"; // components import { BreadcrumbLink } from "@/components/common"; @@ -24,6 +25,8 @@ export const ProjectSettingHeader: FC = observer(() => { const { allowPermissions } = useUserPermissions(); const { loader } = useProject(); + const { t } = useTranslation(); + return (
@@ -65,7 +68,7 @@ export const ProjectSettingHeader: FC = observer(() => { key={item.key} onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}${item.href}`)} > - {item.label} + {t(item.i18n_label)} ) )} diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/sidebar.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/sidebar.tsx index 7a352ce0fe5..b21addee1d4 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/sidebar.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/sidebar.tsx @@ -5,6 +5,7 @@ import range from "lodash/range"; import { observer } from "mobx-react"; import Link from "next/link"; import { useParams, usePathname } from "next/navigation"; +import { useTranslation } from "@plane/i18n"; // ui import { Loader } from "@plane/ui"; // components @@ -21,6 +22,8 @@ export const ProjectSettingsSidebar = observer(() => { // mobx store const { allowPermissions, projectUserInfo } = useUserPermissions(); + const { t } = useTranslation(); + // derived values const currentProjectRole = projectUserInfo?.[workspaceSlug?.toString()]?.[projectId?.toString()]?.role; @@ -58,7 +61,7 @@ export const ProjectSettingsSidebar = observer(() => { isActive={link.highlight(pathname, `/${workspaceSlug}/projects/${projectId}`)} className="text-sm font-medium px-4 py-2" > - {link.label} + {t(link.i18n_label)} ) diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/states/page.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/states/page.tsx index af9bf5618e4..7f20c9079d7 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/states/page.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/settings/states/page.tsx @@ -2,6 +2,7 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; +import { useTranslation } from "@plane/i18n"; // components import { NotAuthorizedView } from "@/components/auth-screens"; import { PageHead } from "@/components/core"; @@ -16,6 +17,8 @@ const StatesSettingsPage = observer(() => { const { currentProjectDetails } = useProject(); const { workspaceUserInfo, allowPermissions } = useUserPermissions(); + const { t } = useTranslation(); + // derived values const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - States` : undefined; // derived values @@ -32,7 +35,7 @@ const StatesSettingsPage = observer(() => { <>
-

States

+

{t("states")}

{workspaceSlug && projectId && ( diff --git a/web/ce/constants/project/settings/tabs.ts b/web/ce/constants/project/settings/tabs.ts index 4d9207cc60a..2d6c0f56480 100644 --- a/web/ce/constants/project/settings/tabs.ts +++ b/web/ce/constants/project/settings/tabs.ts @@ -8,7 +8,7 @@ import { EUserPermissions } from "../../user-permissions"; export const PROJECT_SETTINGS = { general: { key: "general", - label: "General", + i18n_label: "common.general", href: `/settings`, access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`, @@ -16,7 +16,7 @@ export const PROJECT_SETTINGS = { }, members: { key: "members", - label: "Members", + i18n_label: "members", href: `/settings/members`, access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER], highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`, @@ -24,7 +24,7 @@ export const PROJECT_SETTINGS = { }, features: { key: "features", - label: "Features", + i18n_label: "common.features", href: `/settings/features`, access: [EUserPermissions.ADMIN], highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/features/`, @@ -32,15 +32,15 @@ export const PROJECT_SETTINGS = { }, states: { key: "states", - label: "States", + i18n_label: "common.states", href: `/settings/states`, access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER], highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/states/`, Icon: SettingIcon, }, labels: { - key: "labels", - label: "Labels", + key: "i18n_labels", + i18n_label: "common.labels", href: `/settings/labels`, access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER], highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/labels/`, @@ -48,7 +48,7 @@ export const PROJECT_SETTINGS = { }, estimates: { key: "estimates", - label: "Estimates", + i18n_label: "common.estimates", href: `/settings/estimates`, access: [EUserPermissions.ADMIN], highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/estimates/`, @@ -56,7 +56,7 @@ export const PROJECT_SETTINGS = { }, automations: { key: "automations", - label: "Automations", + i18n_label: "common.automations", href: `/settings/automations`, access: [EUserPermissions.ADMIN], highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/automations/`, @@ -66,7 +66,7 @@ export const PROJECT_SETTINGS = { export const PROJECT_SETTINGS_LINKS: { key: string; - label: string; + i18n_label: string; href: string; access: EUserPermissions[]; highlight: (pathname: string, baseUrl: string) => boolean; diff --git a/web/core/components/automation/auto-archive-automation.tsx b/web/core/components/automation/auto-archive-automation.tsx index 800c94e8215..b48543100a9 100644 --- a/web/core/components/automation/auto-archive-automation.tsx +++ b/web/core/components/automation/auto-archive-automation.tsx @@ -55,9 +55,9 @@ export const AutoArchiveAutomation: React.FC = observer((props) => {
-

Auto-archive closed issues

+

{t("project_settings.automations.auto-archive.title")}

- Plane will auto archive issues that have been completed or canceled. + {t("project_settings.automations.auto-archive.description")}

@@ -77,7 +77,9 @@ export const AutoArchiveAutomation: React.FC = observer((props) => { currentProjectDetails.archive_in !== 0 && (
-
Auto-archive issues that are closed for
+
+ {t("project_settings.automations.auto-archive.duration")} +
= observer((props) => { className="flex w-full select-none items-center rounded px-1 py-1.5 text-sm text-custom-text-200 hover:bg-custom-background-80" onClick={() => setmonthModal(true)} > - Customize time range + {t("customize_time_range")} diff --git a/web/core/components/automation/auto-close-automation.tsx b/web/core/components/automation/auto-close-automation.tsx index 9dfd8fcaa68..37cf5f6e251 100644 --- a/web/core/components/automation/auto-close-automation.tsx +++ b/web/core/components/automation/auto-close-automation.tsx @@ -81,9 +81,9 @@ export const AutoCloseAutomation: React.FC = observer((props) => {
-

Auto-close issues

+

{t("project_settings.automations.auto-close.title")}

- Plane will automatically close issues that haven{"'"}t been completed or canceled. + {t("project_settings.automations.auto-close.description")}

@@ -104,7 +104,9 @@ export const AutoCloseAutomation: React.FC = observer((props) => {
-
Auto-close issues that are inactive for
+
+ {t("project_settings.automations.auto-close.duration")} +
= observer((props) => { className="flex w-full select-none items-center rounded px-1 py-1.5 text-custom-text-200 hover:bg-custom-background-80" onClick={() => setmonthModal(true)} > - Customize time range + {t("customize_time_range")} @@ -136,7 +138,9 @@ export const AutoCloseAutomation: React.FC = observer((props) => {
-
Auto-close status
+
+ {t("project_settings.automations.auto-close.auto_close_status")} +
= observer((props) => { )} {selectedOption?.name ? selectedOption.name - : (currentDefaultState?.name ?? State)} + : (currentDefaultState?.name ?? {t("state")})}
} onChange={(val: string) => { diff --git a/web/core/components/estimates/empty-screen.tsx b/web/core/components/estimates/empty-screen.tsx index e3e59a477ea..b05f9f22d16 100644 --- a/web/core/components/estimates/empty-screen.tsx +++ b/web/core/components/estimates/empty-screen.tsx @@ -7,6 +7,7 @@ import { Button } from "@plane/ui"; // public images import EstimateEmptyDarkImage from "@/public/empty-state/estimates/dark.svg"; import EstimateEmptyLightImage from "@/public/empty-state/estimates/light.svg"; +import { useTranslation } from "@plane/i18n"; type TEstimateEmptyScreen = { onButtonClick: () => void; @@ -17,6 +18,8 @@ export const EstimateEmptyScreen: FC = (props) => { const { onButtonClick } = props; const { resolvedTheme } = useTheme(); + const { t } = useTranslation(); + const emptyScreenImage = resolvedTheme === "light" ? EstimateEmptyLightImage : EstimateEmptyDarkImage; return ( @@ -31,13 +34,13 @@ export const EstimateEmptyScreen: FC = (props) => { />
-

No estimate systems yet

-

- Create a set of estimates to communicate the amount of work per issue. -

+

+ {t("project_settings.empty_state.estimates.title")} +

+

{t("project_settings.empty_state.estimates.description")}

- +
); diff --git a/web/core/components/estimates/root.tsx b/web/core/components/estimates/root.tsx index 873ed410088..38abba9b376 100644 --- a/web/core/components/estimates/root.tsx +++ b/web/core/components/estimates/root.tsx @@ -14,6 +14,7 @@ import { import { useProject, useProjectEstimates } from "@/hooks/store"; // plane web components import { UpdateEstimateModal } from "@/plane-web/components/estimates"; +import { useTranslation } from "@plane/i18n"; type TEstimateRoot = { workspaceSlug: string; @@ -31,6 +32,8 @@ export const EstimateRoot: FC = observer((props) => { const [estimateToUpdate, setEstimateToUpdate] = useState(); const [estimateToDelete, setEstimateToDelete] = useState(); + const { t } = useTranslation(); + const { isLoading: isSWRLoading } = useSWR( workspaceSlug && projectId ? `PROJECT_ESTIMATES_${workspaceSlug}_${projectId}` : null, async () => workspaceSlug && projectId && getProjectEstimates(workspaceSlug, projectId) @@ -44,7 +47,7 @@ export const EstimateRoot: FC = observer((props) => {
{/* header */}
-

Estimates

+

{t("common.estimates")}

{/* current active estimate section */} @@ -53,10 +56,8 @@ export const EstimateRoot: FC = observer((props) => { {/* estimates activated deactivated section */}
-

Enable estimates for my project

-

- They help you in communicating complexity and workload of the team. -

+

{t("project_settings.estimates.title")}

+

{t("project_settings.estimates.description")}

diff --git a/web/core/components/labels/create-update-label-inline.tsx b/web/core/components/labels/create-update-label-inline.tsx index f5941ca40c6..ddbe220b933 100644 --- a/web/core/components/labels/create-update-label-inline.tsx +++ b/web/core/components/labels/create-update-label-inline.tsx @@ -8,6 +8,7 @@ import { Controller, SubmitHandler, useForm } from "react-hook-form"; import { Popover, Transition } from "@headlessui/react"; // plane imports import { getRandomLabelColor, LABEL_COLOR_OPTIONS } from "@plane/constants"; +import { useTranslation } from "@plane/i18n"; import { IIssueLabel } from "@plane/types"; import { Button, Input, TOAST_TYPE, setToast } from "@plane/ui"; // hooks @@ -46,6 +47,8 @@ export const CreateUpdateLabelInline = observer( defaultValues, }); + const { t } = useTranslation(); + const handleClose = () => { setLabelForm(false); reset(defaultValues); @@ -64,7 +67,7 @@ export const CreateUpdateLabelInline = observer( setToast({ title: "Error!", type: TOAST_TYPE.ERROR, - message: error?.detail ?? error.error ?? "Something went wrong. Please try again later.", + message: error?.detail ?? error.error ?? t("common.something_went_wrong"), }); reset(formData); }); @@ -83,7 +86,7 @@ export const CreateUpdateLabelInline = observer( setToast({ title: "Oops!", type: TOAST_TYPE.ERROR, - message: error?.error ?? "Error while updating the label", + message: error?.error ?? t("project_settings.labels.toast.error"), }); reset(formData); }); @@ -171,10 +174,10 @@ export const CreateUpdateLabelInline = observer( control={control} name="name" rules={{ - required: "Label title is required", + required: t("project_settings.labels.label_title_is_required"), maxLength: { value: 255, - message: "Label name should not exceed 255 characters", + message: t("project_settings.labels.label_max_char"), }, }} render={({ field: { value, onChange, ref } }) => ( @@ -187,17 +190,17 @@ export const CreateUpdateLabelInline = observer( onChange={onChange} ref={ref} hasError={Boolean(errors.name)} - placeholder="Label title" + placeholder={t("project_settings.labels.label_title")} className="w-full" /> )} />
{errors.name?.message &&

{errors.name?.message}

} diff --git a/web/core/components/labels/project-setting-label-list.tsx b/web/core/components/labels/project-setting-label-list.tsx index e0943da3769..5a25face14a 100644 --- a/web/core/components/labels/project-setting-label-list.tsx +++ b/web/core/components/labels/project-setting-label-list.tsx @@ -73,7 +73,7 @@ export const ProjectSettingsLabelList: React.FC = observer(() => {

Labels

{isEditable && ( )}
diff --git a/web/core/components/project/form.tsx b/web/core/components/project/form.tsx index 85328121a6e..2e15d47e761 100644 --- a/web/core/components/project/form.tsx +++ b/web/core/components/project/form.tsx @@ -101,14 +101,14 @@ export const ProjectDetailsForm: FC = (props) => { payload: { ...res, changed_properties: changed_properties, - state: "SUCCESS", + state: t("toast.success"), element: "Project general settings", }, }); setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success!", - message: "Project updated successfully", + title: t("toast.success"), + message: t("project_settings.general.toast.success"), }); }) .catch((error) => { @@ -118,8 +118,8 @@ export const ProjectDetailsForm: FC = (props) => { }); setToast({ type: TOAST_TYPE.ERROR, - title: "Error!", - message: error?.error ?? "Project could not be updated. Please try again.", + title: t("toast.error"), + message: error?.error ?? t("project_settings.general.toast.error"), }); }); }; @@ -146,7 +146,7 @@ export const ProjectDetailsForm: FC = (props) => { await projectService .checkProjectIdentifierAvailability(workspaceSlug as string, payload.identifier ?? "") .then(async (res) => { - if (res.exists) setError("identifier", { message: "Identifier already exists" }); + if (res.exists) setError("identifier", { message: t("common.identifier_already_exists") }); else await handleUpdateChange(payload); }); else await handleUpdateChange(payload); @@ -219,7 +219,7 @@ export const ProjectDetailsForm: FC = (props) => { name="cover_image_url" render={({ field: { value, onChange } }) => ( = (props) => {
-

Project name

+

{t("common.project_name")}

= (props) => { onChange={onChange} hasError={Boolean(errors.name)} className="rounded-md !p-3 font-medium" - placeholder="Project name" + placeholder={t("common.project_name")} disabled={!isAdmin} /> )} @@ -263,7 +263,7 @@ export const ProjectDetailsForm: FC = (props) => { {errors?.name?.message}
-

Description

+

{t("description")}

= (props) => { id="description" name="description" value={value} - placeholder="Enter project description" + placeholder={t("project_description_placeholder")} onChange={onChange} className="min-h-[102px] text-sm font-medium" hasError={Boolean(errors?.description)} @@ -289,17 +289,15 @@ export const ProjectDetailsForm: FC = (props) => { control={control} name="identifier" rules={{ - required: "Project ID is required", - validate: (value) => - /^[ÇŞĞIİÖÜA-Z0-9]+$/.test(value.toUpperCase()) || - "Only Alphanumeric & Non-latin characters are allowed.", + required: t("project_id_is_required"), + validate: (value) => /^[ÇŞĞIİÖÜA-Z0-9]+$/.test(value.toUpperCase()) || t("project_id_allowed_char"), minLength: { value: 1, - message: "Project ID must at least be of 1 character", + message: t("project_id_min_char"), }, maxLength: { value: 5, - message: "Project ID must at most be of 5 characters", + message: t("project_id_max_char"), }, }} render={({ field: { value, ref } }) => ( @@ -311,7 +309,7 @@ export const ProjectDetailsForm: FC = (props) => { onChange={handleIdentifierChange} ref={ref} hasError={Boolean(errors.identifier)} - placeholder="Enter project ID" + placeholder={t("project_settings.general.enter_project_id")} className="w-full font-medium" disabled={!isAdmin} /> @@ -331,7 +329,7 @@ export const ProjectDetailsForm: FC = (props) => {
-

Network

+

{t("network")}

= (props) => { {t(selectedNetwork.i18n_label)} ) : ( - Select network + {t("select_network")} )}
} @@ -375,11 +373,11 @@ export const ProjectDetailsForm: FC = (props) => { />
-

Project Timezone

+

{t("common.project_timezone")}

( <> = (props) => {
<> - Created on {renderFormattedDate(project?.created_at)} + {t("common.created_on")} {renderFormattedDate(project?.created_at)}
diff --git a/web/core/components/project/member-list.tsx b/web/core/components/project/member-list.tsx index 53be9fc684d..2644d574676 100644 --- a/web/core/components/project/member-list.tsx +++ b/web/core/components/project/member-list.tsx @@ -6,6 +6,7 @@ import { useParams } from "next/navigation"; import { Search } from "lucide-react"; // hooks // components +import { useTranslation } from "@plane/i18n"; import { Button } from "@plane/ui"; import { ProjectMemberListItem, SendProjectInvitationModal } from "@/components/project"; // ui @@ -26,6 +27,9 @@ export const ProjectMemberList: React.FC = observer(() => { project: { projectMemberIds, getProjectMemberDetails }, } = useMember(); const { allowPermissions } = useUserPermissions(); + + const { t } = useTranslation(); + const searchedMembers = (projectMemberIds ?? []).filter((userId) => { const memberDetails = projectId ? getProjectMemberDetails(userId, projectId.toString()) : null; @@ -47,7 +51,7 @@ export const ProjectMemberList: React.FC = observer(() => { setInviteModal(false)} />
-

Members

+

{t("members")}

{ setInviteModal(true); }} > - Add member + {t("add_member")} )}
@@ -77,7 +81,7 @@ export const ProjectMemberList: React.FC = observer(() => { {searchedMembers.length !== 0 && } {searchedMembers.length === 0 && ( -

No matching members

+

{t("no_matching_members")}

)}
)} diff --git a/web/core/components/project/project-settings-member-defaults.tsx b/web/core/components/project/project-settings-member-defaults.tsx index 4b6c3cd390d..fc408781315 100644 --- a/web/core/components/project/project-settings-member-defaults.tsx +++ b/web/core/components/project/project-settings-member-defaults.tsx @@ -5,6 +5,7 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { Controller, useForm } from "react-hook-form"; import useSWR from "swr"; +import { useTranslation } from "@plane/i18n"; import { IProject, IUserLite, IWorkspace } from "@plane/types"; // ui import { Loader, TOAST_TYPE, ToggleSwitch, setToast } from "@plane/ui"; @@ -29,6 +30,8 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => { // store hooks const { allowPermissions } = useUserPermissions(); + const { t } = useTranslation(); + const { currentProjectDetails, fetchProjectDetails, updateProject } = useProject(); // derived values const isAdmin = allowPermissions( @@ -78,9 +81,9 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => { }) .then(() => { setToast({ - title: "Success!", + title: `${t("success")}!`, type: TOAST_TYPE.SUCCESS, - message: "Project updated successfully", + message: t("project_settings.general.toast.success"), }); }) .catch((err) => { @@ -96,9 +99,9 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => { }) .then(() => { setToast({ - title: "Success!", + title: `${t("success")}!`, type: TOAST_TYPE.SUCCESS, - message: "Project updated successfully", + message: t("project_settings.general.toast.success"), }); }) .catch((err) => { @@ -109,13 +112,13 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => { return ( <>
-

Defaults

+

{t("defaults")}

-

Project lead

+

{t("project_settings.members.project_lead")}

{currentProjectDetails ? ( {
-

Default assignee

+

{t("project_settings.members.default_assignee")}

{currentProjectDetails ? ( {

- Grant view access to all issues for guest users: + {t("project_settings.members.guest_super_permissions.title")}

- This will allow guests to have view access to all the project issues. + {t("project_settings.members.guest_super_permissions.sub_heading")}

= observer((props) => { name: "members", }); + const { t } = useTranslation(); + const currentProjectRole = projectUserInfo?.[workspaceSlug?.toString()]?.[projectId?.toString()]?.role; const uninvitedPeople = workspaceMemberIds?.filter((userId) => { @@ -212,10 +215,12 @@ export const SendProjectInvitationModal: React.FC = observer((props) => {
- Invite members + {t("project_settings.members.invite_members.title")}
-

Invite members to work on your project.

+

+ {t("project_settings.members.invite_members.sub_heading")} +

@@ -339,16 +344,16 @@ export const SendProjectInvitationModal: React.FC = observer((props) => { onClick={appendField} > - Add more + {t("common.add_more")}
From 1479585271d587698ae567a576bfa7b4f8ffc403 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Wed, 29 Jan 2025 15:36:40 +0530 Subject: [PATCH 2/2] chore: code refactor --- web/core/components/project/form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/core/components/project/form.tsx b/web/core/components/project/form.tsx index 2e15d47e761..0e62b2fe83d 100644 --- a/web/core/components/project/form.tsx +++ b/web/core/components/project/form.tsx @@ -101,7 +101,7 @@ export const ProjectDetailsForm: FC = (props) => { payload: { ...res, changed_properties: changed_properties, - state: t("toast.success"), + state: "SUCCESS", element: "Project general settings", }, });