From e7d3ae949d8ea1663f32392566c260f75154ed99 Mon Sep 17 00:00:00 2001 From: Vamsi krishna Date: Fri, 17 Jan 2025 12:38:25 +0530 Subject: [PATCH 1/2] * improvement: added translation to issues - lists, headers --- .../i18n/src/locales/en/translations.json | 81 ++++++++++++- .../i18n/src/locales/es/translations.json | 81 ++++++++++++- .../i18n/src/locales/fr/translations.json | 81 ++++++++++++- .../i18n/src/locales/ja/translations.json | 81 ++++++++++++- .../[projectId]/issues/(detail)/header.tsx | 4 +- .../issues/(list)/mobile-header.tsx | 11 +- .../[projectId]/issues/(list)/page.tsx | 9 +- web/ce/components/issues/header.tsx | 11 +- .../issues/worklog/activity/filter-root.tsx | 1 + web/ce/constants/issues.ts | 20 ++-- .../core/filters/date-filter-modal.tsx | 10 +- .../core/filters/date-filter-select.tsx | 16 ++- .../dropdowns/cycle/cycle-options.tsx | 14 ++- web/core/components/dropdowns/cycle/index.tsx | 6 +- .../components/dropdowns/module/index.tsx | 14 ++- .../dropdowns/module/module-options.tsx | 13 +- web/core/components/dropdowns/state.tsx | 2 +- .../create-issue-toast-action-items.tsx | 8 +- web/core/components/issues/filters.tsx | 8 +- .../issue-activity/activity-filter.tsx | 6 +- .../issue-detail/issue-activity/root.tsx | 39 +++--- .../filters/applied-filters/filters-list.tsx | 6 +- .../display-filters/display-properties.tsx | 6 +- .../header/display-filters/extra-options.tsx | 6 +- .../header/display-filters/group-by.tsx | 7 +- .../header/display-filters/issue-grouping.tsx | 7 +- .../header/display-filters/order-by.tsx | 8 +- .../header/display-filters/sub-group-by.tsx | 5 +- .../filters/header/filters/assignee.tsx | 9 +- .../filters/header/filters/created-by.tsx | 11 +- .../filters/header/filters/cycle.tsx | 10 +- .../header/filters/filters-selection.tsx | 5 +- .../filters/header/filters/labels.tsx | 10 +- .../filters/header/filters/mentions.tsx | 11 +- .../filters/header/filters/module.tsx | 9 +- .../filters/header/filters/priority.tsx | 10 +- .../filters/header/filters/project.tsx | 9 +- .../filters/header/filters/start-date.tsx | 14 ++- .../filters/header/filters/state-group.tsx | 10 +- .../filters/header/filters/state.tsx | 9 +- .../filters/header/filters/target-date.tsx | 14 ++- .../filters/header/layout-selection.tsx | 4 +- .../issue-layouts/quick-add/button/gantt.tsx | 5 +- .../issue-layouts/quick-add/button/kanban.tsx | 5 +- .../issue-layouts/quick-add/button/list.tsx | 5 +- .../quick-add/button/spreadsheet.tsx | 5 +- .../issue-layouts/quick-add/form/calendar.tsx | 7 +- .../issue-layouts/quick-add/form/gantt.tsx | 9 +- .../issue-layouts/quick-add/form/kanban.tsx | 9 +- .../issue-layouts/quick-add/form/list.tsx | 9 +- .../quick-add/form/spreadsheet.tsx | 9 +- .../issues/issue-layouts/quick-add/root.tsx | 37 +++--- .../issues/issue-layouts/save-filter-view.tsx | 4 +- .../components/issues/issue-modal/form.tsx | 2 +- .../issues/parent-issues-list-modal.tsx | 8 +- .../components/issues/peek-overview/root.tsx | 15 ++- .../project/applied-filters/date.tsx | 5 +- .../project/dropdowns/filters/created-at.tsx | 9 +- web/core/constants/filters.ts | 11 ++ web/core/constants/issue.ts | 113 +++++++++++------- 60 files changed, 722 insertions(+), 241 deletions(-) diff --git a/packages/i18n/src/locales/en/translations.json b/packages/i18n/src/locales/en/translations.json index 596c3093fa9..f0fcc1a785b 100644 --- a/packages/i18n/src/locales/en/translations.json +++ b/packages/i18n/src/locales/en/translations.json @@ -74,7 +74,7 @@ "dark": "Dark", "light_contrast": "Light high contrast", "dark_contrast": "Dark high contrast", - "custom": "Custom theme", + "custom": "Custom", "select_your_theme": "Select your theme", "customize_your_theme": "Customize your theme", "background_color": "Background color", @@ -316,5 +316,82 @@ "change_parent_issue": "Change parent issue", "remove_parent_issue": "Remove parent issue", "add_parent": "Add parent", - "loading_members": "Loading members..." + "loading_members": "Loading members...", + "issue": "Issue", + "layout": "Layout", + "filters": "Filters", + "display": "Display", + "display_properties": "Display properties", + "list_layout": "List layout", + "board_layout": "Board layout", + "calendar_layout": "Calendar layout", + "table_layout": "Table layout", + "timeline_layout": "Timeline layout", + "no_cycle": "No cycle", + "no_matches_found": "No matches found", + "module": "Module", + "no_module": "No module", + "state_group": "State group", + "view_less": "View less", + "view_all": "View all", + "mention": "Mention", + "created_by": "Created by", + "label": "Label", + "epic": "Epic", + "epics": "Epics", + "grouping": "Grouping", + "apply": "Apply", + "after": "After", + "before": "Before", + "range": "Range", + "all": "All", + "active": "Active", + "backlog": "Backlog", + "target_date": "Due date", + "search_results_for": "Search results for", + "in_project": "In project", + "type_to_search": "Type to search...", + "1_week_ago": "1 week ago", + "2_weeks_ago": "2 weeks ago", + "1_month_ago": "1 month ago", + "2_months_ago": "2 months ago", + "1_week_from_now": "1 week from now", + "2_weeks_from_now": "2 weeks from now", + "1_month_from_now": "1 month from now", + "2_months_from_now": "2 months from now", + "id": "ID", + "sub_issue_count": "Sub issue count", + "attachment_count": "Attachment count", + "link": "Link", + "group_by": "Group by", + "sub_group_by": "Sub-group by", + "states": "States", + "manual": "Manual", + "last_created": "Last created", + "last_updated": "Last updated", + "order_by": "Order by", + "show_sub_issues": "Show sub-issues", + "show_empty_groups": "Show empty groups", + "clear_all": "Clear all", + "save_view": "Save view", + "press_enter_to_add_another": "Press 'Enter' to add another", + "epic_title": "Epic title", + "issue_title": "Issue title", + "new": "New", + "created_successfully": "Created successfully", + "view": "View", + "copied": "Copied", + "your_issue_can_be_found_in_project_issues": "Your issue can be found in project issues.", + "restore_success": "Restore success", + "issue_could_not_be_restored_please_try_again": "Issue could not be restored. Please try again.", + "issue_delete_failed": "Issue delete failed", + "updates": "Updates", + "comment_created_successfully": "Comment created successfully", + "comment_creation_failed_please_try_again_later": "Comment creation failed. Please try again later.", + "missing_fields": "Missing fields", + "comment_updated_successfully": "Comment updated successfully", + "comment_update_failed_please_try_again_later": "Comment update failed. Please try again later.", + "comment_removed_successfully": "Comment removed successfully", + "comment_remove_failed_please_try_again_later": "Comment remove failed. Please try again later.", + "asset_upload_failed_please_try_again_later": "Asset upload failed. Please try again later." } diff --git a/packages/i18n/src/locales/es/translations.json b/packages/i18n/src/locales/es/translations.json index 9f2b98792c2..74d4999af30 100644 --- a/packages/i18n/src/locales/es/translations.json +++ b/packages/i18n/src/locales/es/translations.json @@ -73,7 +73,7 @@ "dark": "Oscuro", "light_contrast": "Alto contraste claro", "dark_contrast": "Alto contraste oscuro", - "custom": "Tema personalizado", + "custom": "Personalizado", "select_your_theme": "Selecciona tu tema", "customize_your_theme": "Personaliza tu tema", "background_color": "Color de fondo", @@ -316,5 +316,82 @@ "remove_parent_issue": "Eliminar problema padre", "add_parent": "Agregar padre", "loading_members": "Cargando miembros...", - "inbox": "bandeja de entrada" + "inbox": "bandeja de entrada", + "issue": "Problema", + "layout": "Diseño", + "filters": "Filtros", + "display": "Mostrar", + "display_properties": "Propiedades de visualización", + "list_layout": "Diseño de lista", + "board_layout": "Diseño de tabla", + "calendar_layout": "Diseño de calendario", + "table_layout": "Diseño de tabla", + "timeline_layout": "Diseño de cronología", + "no_cycle": "Sin ciclo", + "no_matches_found": "No hay resultados coincidentes", + "module": "Módulo", + "no_module": "Sin módulo", + "state_group": "Grupo de estado", + "view_less": "Ver menos", + "view_all": "Ver todo", + "mention": "Mención", + "created_by": "Creado por", + "label": "Etiqueta", + "epic": "Epic", + "epics": "Epics", + "grouping": "Agrupación", + "apply": "Aplicar", + "after": "Después", + "before": "Antes", + "range": "Rango", + "all": "Todos", + "active": "Activo", + "backlog": "Backlog", + "target_date": "Fecha de vencimiento", + "search_results_for": "Resultados de la búsqueda", + "in_project": "En el proyecto", + "type_to_search": "Buscar...", + "1_week_ago": "1 semana atrás", + "2_weeks_ago": "2 semanas atrás", + "1_month_ago": "1 mes atrás", + "2_months_ago": "2 meses atrás", + "1_week_from_now": "1 semana después", + "2_weeks_from_now": "2 semanas después", + "1_month_from_now": "1 mes después", + "2_months_from_now": "2 meses después", + "id": "ID", + "sub_issue_count": "Número de subproblemas", + "attachment_count": "Número de archivos adjuntos", + "link": "Enlace", + "group_by": "Agrupar por", + "sub_group_by": "Subagrupar por", + "states": "Estados", + "manual": "Manual", + "last_created": "Último creado", + "last_updated": "Último actualizado", + "order_by": "Ordenar por", + "show_sub_issues": "Mostrar subproblemas", + "show_empty_groups": "Mostrar grupos vacíos", + "clear_all": "Limpiar todo", + "save_view": "Guardar vista", + "press_enter_to_add_another": "Presiona 'Enter' para agregar otro", + "epic_title": "Título de epic", + "issue_title": "Título de problema", + "new": "Nuevo", + "created_successfully": "Creado con éxito", + "view": "Ver", + "copied": "Copiado", + "your_issue_can_be_found_in_project_issues": "Tu problema puede ser encontrado en los problemas del proyecto.", + "restore_success": "Restauración exitosa", + "issue_could_not_be_restored_please_try_again": "El problema no pudo ser restaurado. Por favor, inténtalo de nuevo.", + "issue_delete_failed": "El problema no pudo ser eliminado. Por favor, inténtalo de nuevo.", + "updates": "Actualizaciones", + "comment_created_successfully": "Comentario creado con éxito", + "comment_creation_failed_please_try_again_later": "Error al crear el comentario. Por favor, inténtalo de nuevo más tarde.", + "missing_fields": "Faltan campos", + "comment_updated_successfully": "Comentario actualizado con éxito", + "comment_update_failed_please_try_again_later": "Error al actualizar el comentario. Por favor, inténtalo de nuevo más tarde.", + "comment_removed_successfully": "Comentario eliminado con éxito", + "comment_remove_failed_please_try_again_later": "Error al eliminar el comentario. Por favor, inténtalo de nuevo más tarde.", + "asset_upload_failed_please_try_again_later": "Error al subir el archivo. Por favor, inténtalo de nuevo más tarde." } diff --git a/packages/i18n/src/locales/fr/translations.json b/packages/i18n/src/locales/fr/translations.json index 0eee868e174..3f67a1d7c84 100644 --- a/packages/i18n/src/locales/fr/translations.json +++ b/packages/i18n/src/locales/fr/translations.json @@ -73,7 +73,7 @@ "dark": "Foncé", "light_contrast": "Clair de haut contraste", "dark_contrast": "Foncé de haut contraste", - "custom": "Thème personnalisé", + "custom": "Personnalisé", "select_your_theme": "Sélectionnez votre thème", "customize_your_theme": "Personnalisez votre thème", "background_color": "Couleur de fond", @@ -316,5 +316,82 @@ "remove_parent_issue": "Supprimer le problème parent", "add_parent": "Ajouter un parent", "loading_members": "Chargement des membres...", - "inbox": "Boîte de réception" + "inbox": "Boîte de réception", + "issue": "Problème", + "layout": "Disposition", + "filters": "Filtres", + "display": "Affichage", + "display_properties": "Propriétés d'affichage", + "list_layout": "Disposition de liste", + "board_layout": "Disposition de tableau", + "calendar_layout": "Disposition de calendrier", + "table_layout": "Disposition de tableau", + "timeline_layout": "Disposition de chronologie", + "no_cycle": "Aucun cycle", + "no_matches_found": "Aucun résultat correspondant", + "module": "Module", + "no_module": "Aucun module", + "state_group": "Groupe d'état", + "view_less": "Voir moins", + "view_all": "Voir tout", + "mention": "Mention", + "created_by": "Créé par", + "label": "Étiquette", + "epic": "Épique", + "epics": "Épiques", + "grouping": "Groupement", + "apply": "Appliquer", + "after": "Après", + "before": "Avant", + "range": "Plage", + "all": "Tous", + "active": "Actif", + "backlog": "Backlog", + "target_date": "Date d'échéance", + "search_results_for": "Résultats de la recherche", + "in_project": "Dans le projet", + "type_to_search": "Rechercher...", + "1_week_ago": "1 semaine avant", + "2_weeks_ago": "2 semaines avant", + "1_month_ago": "1 mois avant", + "2_months_ago": "2 mois avant", + "1_week_from_now": "1 semaine après", + "2_weeks_from_now": "2 semaines après", + "1_month_from_now": "1 mois après", + "2_months_from_now": "2 mois après", + "id": "ID", + "sub_issue_count": "Nombre de sous-problèmes", + "attachment_count": "Nombre d'attachments", + "link": "Lien", + "group_by": "Groupement", + "sub_group_by": "Sous-groupement", + "states": "États", + "manual": "Manuel", + "last_created": "Dernier créé", + "last_updated": "Dernier mis à jour", + "order_by": "Trier par", + "show_sub_issues": "Afficher les sous-problèmes", + "show_empty_groups": "Afficher les groupes vides", + "clear_all": "Tout effacer", + "save_view": "Enregistrer la vue", + "press_enter_to_add_another": "'Entrer' pour ajouter un autre", + "epic_title": "Titre de l'épique", + "issue_title": "Titre du problème", + "new": "Nouveau", + "created_successfully": "Créé avec succès", + "view": "Voir", + "copied": "Copié", + "your_issue_can_be_found_in_project_issues": "Le problème peut être trouvé dans les problèmes du projet.", + "restore_success": "Restauration réussie", + "issue_could_not_be_restored_please_try_again": "Le problème n'a pas pu être restauré. Veuillez réessayer.", + "issue_delete_failed": "Le problème n'a pas pu être supprimé. Veuillez réessayer.", + "updates": "Mises à jour", + "comment_created_successfully": "Commentaire créé avec succès", + "comment_creation_failed_please_try_again_later": "Erreur lors de la création du commentaire. Veuillez réessayer plus tard.", + "missing_fields": "Champs manquants", + "comment_updated_successfully": "Commentaire mis à jour avec succès", + "comment_update_failed_please_try_again_later": "Erreur lors de la mise à jour du commentaire. Veuillez réessayer plus tard.", + "comment_removed_successfully": "Commentaire supprimé avec succès", + "comment_remove_failed_please_try_again_later": "Erreur lors de la suppression du commentaire. Veuillez réessayer plus tard.", + "asset_upload_failed_please_try_again_later": "Erreur lors de l'upload de l'asset. Veuillez réessayer plus tard." } diff --git a/packages/i18n/src/locales/ja/translations.json b/packages/i18n/src/locales/ja/translations.json index fa2b244cc5e..d62f3555b6d 100644 --- a/packages/i18n/src/locales/ja/translations.json +++ b/packages/i18n/src/locales/ja/translations.json @@ -73,7 +73,7 @@ "dark": "ダーク", "light_contrast": "ライト高コントラスト", "dark_contrast": "ダーク高コントラスト", - "custom": "カスタムテーマ", + "custom": "カスタム", "select_your_theme": "テーマを選択", "customize_your_theme": "テーマをカスタマイズ", "background_color": "背景色", @@ -316,5 +316,82 @@ "remove_parent_issue": "親問題を削除", "add_parent": "親問題を追加", "loading_members": "メンバーを読み込んでいます...", - "inbox": "受信箱" + "inbox": "受信箱", + "issue": "問題", + "layout": "レイアウト", + "filters": "フィルター", + "display": "表示", + "display_properties": "表示プロパティ", + "list_layout": "リストレイアウト", + "board_layout": "ボードレイアウト", + "calendar_layout": "カレンダーレイアウト", + "table_layout": "テーブルレイアウト", + "timeline_layout": "タイムラインレイアウト", + "no_cycle": "サイクルなし", + "no_matches_found": "一致する結果はありません", + "module": "モジュール", + "no_module": "モジュールなし", + "state_group": "ステータスグループ", + "view_less": "少なく見る", + "view_all": "すべて見る", + "mention": "メンション", + "created_by": "作成者", + "label": "ラベル", + "epic": "エピック", + "epics": "エピック", + "grouping": "グループ化", + "apply": "適用", + "after": "後", + "before": "前", + "range": "範囲", + "all": "すべて", + "active": "アクティブ", + "backlog": "バックログ", + "target_date": "期限日", + "search_results_for": "検索結果", + "in_project": "プロジェクト", + "type_to_search": "検索...", + "1_week_ago": "1週間前", + "2_weeks_ago": "2週間前", + "1_month_ago": "1ヶ月前", + "2_months_ago": "2ヶ月前", + "1_week_from_now": "1週間後", + "2_weeks_from_now": "2週間後", + "1_month_from_now": "1ヶ月後", + "2_months_from_now": "2ヶ月後", + "id": "ID", + "sub_issue_count": "サブ問題数", + "attachment_count": "添付ファイル数", + "link": "リンク", + "group_by": "グループ化", + "sub_group_by": "サブグループ化", + "states": "ステータス", + "manual": "手動", + "last_created": "最後に作成", + "last_updated": "最後に更新", + "order_by": "並び替え", + "show_sub_issues": "サブ問題を表示", + "show_empty_groups": "空のグループを表示", + "clear_all": "すべてクリア", + "save_view": "ビューを保存", + "press_enter_to_add_another": "'Enter'を押して別の", + "epic_title": "エピックタイトル", + "issue_title": "問題タイトル", + "new": "新しい", + "created_successfully": "正常に作成されました", + "view": "表示", + "copied": "コピーされました", + "your_issue_can_be_found_in_project_issues": "問題はプロジェクトの問題で見つかります。", + "restore_success": "復元成功", + "issue_could_not_be_restored_please_try_again": "問題を復元できませんでした。もう一度お試しください。", + "issue_delete_failed": "問題の削除に失敗しました。", + "updates": "更新", + "comment_created_successfully": "コメントが正常に作成されました", + "comment_creation_failed_please_try_again_later": "コメントの作成に失敗しました。もう一度お試しください。", + "missing_fields": "フィールドが不足しています", + "comment_updated_successfully": "コメントが正常に更新されました", + "comment_update_failed_please_try_again_later": "コメントの更新に失敗しました。もう一度お試しください。", + "comment_removed_successfully": "コメントが正常に削除されました", + "comment_remove_failed_please_try_again_later": "コメントの削除に失敗しました。もう一度お試しください。", + "asset_upload_failed_please_try_again_later": "アセットのアップロードに失敗しました。もう一度お試しください。" } diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(detail)/header.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(detail)/header.tsx index 44239006eea..06e47e2dc7a 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(detail)/header.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(detail)/header.tsx @@ -3,6 +3,7 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // ui +import { useTranslation } from "@plane/i18n"; import { Breadcrumbs, LayersIcon, Header } from "@plane/ui"; // components import { BreadcrumbLink } from "@/components/common"; @@ -14,6 +15,7 @@ import { useAppRouter } from "@/hooks/use-app-router"; import { ProjectBreadcrumb } from "@/plane-web/components/breadcrumbs"; export const ProjectIssueDetailsHeader = observer(() => { + const { t } = useTranslation(); // router const router = useAppRouter(); const { workspaceSlug, projectId, issueId } = useParams(); @@ -37,7 +39,7 @@ export const ProjectIssueDetailsHeader = observer(() => { link={ } /> } diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/mobile-header.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/mobile-header.tsx index 29f2637b76e..d07d49a012e 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/mobile-header.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/mobile-header.tsx @@ -7,6 +7,7 @@ import { useParams } from "next/navigation"; import { Calendar, ChevronDown, Kanban, List } from "lucide-react"; // plane constants import { EIssueLayoutTypes, EIssueFilterType, EIssuesStoreType } from "@plane/constants"; +import { useTranslation } from "@plane/i18n"; // types import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions } from "@plane/types"; // ui @@ -53,6 +54,8 @@ export const ProjectIssuesMobileHeader = observer(() => { [workspaceSlug, projectId, updateFilters] ); + const { t } = useTranslation(); + const handleFiltersUpdate = useCallback( (key: keyof IIssueFilterOptions, value: string | string[]) => { if (!workspaceSlug || !projectId) return; @@ -104,7 +107,7 @@ export const ProjectIssuesMobileHeader = observer(() => { placement="bottom-start" customButton={
- Layout + {t("layout")}
} @@ -130,7 +133,7 @@ export const ProjectIssuesMobileHeader = observer(() => { placement="bottom-end" menuButton={ - Filters + {t("filters")} } @@ -158,7 +161,7 @@ export const ProjectIssuesMobileHeader = observer(() => { placement="bottom-end" menuButton={ - Display + {t("display")} } @@ -181,7 +184,7 @@ export const ProjectIssuesMobileHeader = observer(() => { onClick={() => setAnalyticsModal(true)} className="flex flex-grow justify-center border-l border-custom-border-200 text-sm text-custom-text-200" > - Analytics + {t("analytics")} diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/page.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/page.tsx index 7ea73bb1c94..02406d170ec 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/page.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/page.tsx @@ -3,6 +3,7 @@ import { observer } from "mobx-react"; import Head from "next/head"; import { useParams } from "next/navigation"; +import { useTranslation } from "@plane/i18n"; // components import { PageHead } from "@/components/core"; import { ProjectLayoutRoot } from "@/components/issues"; @@ -13,20 +14,22 @@ const ProjectIssuesPage = observer(() => { const { projectId } = useParams(); // store const { getProjectById } = useProject(); - + const { t } = useTranslation(); if (!projectId) { return <>; } // derived values const project = getProjectById(projectId.toString()); - const pageTitle = project?.name ? `${project?.name} - Issues` : undefined; + const pageTitle = project?.name ? `${project?.name} - ${t("issues")}` : undefined; return ( <> - {project?.name} - Issues + + {project?.name} - {t("issues")} +
diff --git a/web/ce/components/issues/header.tsx b/web/ce/components/issues/header.tsx index f9e82b095ae..c8d6d2b897d 100644 --- a/web/ce/components/issues/header.tsx +++ b/web/ce/components/issues/header.tsx @@ -6,6 +6,7 @@ import { useParams } from "next/navigation"; import { Circle, ExternalLink } from "lucide-react"; // plane constants import { EIssuesStoreType } from "@plane/constants"; +import { useTranslation } from "@plane/i18n"; // ui import { Breadcrumbs, Button, LayersIcon, Tooltip, Header } from "@plane/ui"; // components @@ -27,6 +28,7 @@ export const IssuesHeader = observer(() => { // router const router = useAppRouter(); const { workspaceSlug, projectId } = useParams() as { workspaceSlug: string; projectId: string }; + const { t } = useTranslation(); // store hooks const { issues: { getGroupIssueCount }, @@ -57,12 +59,15 @@ export const IssuesHeader = observer(() => { } />} + link={ + } /> + } /> {issuesCount && issuesCount > 0 ? ( 1 ? "issues" : "issue"} in this project`} position="bottom" > @@ -78,7 +83,7 @@ export const IssuesHeader = observer(() => { rel="noopener noreferrer" > - Public + {t("public")} ) : ( @@ -102,7 +107,7 @@ export const IssuesHeader = observer(() => { }} size="sm" > -
Add
Issue +
{t("add")}
{t("issue")} ) : ( <> diff --git a/web/ce/components/issues/worklog/activity/filter-root.tsx b/web/ce/components/issues/worklog/activity/filter-root.tsx index 2d11ae34148..5368f3a4f24 100644 --- a/web/ce/components/issues/worklog/activity/filter-root.tsx +++ b/web/ce/components/issues/worklog/activity/filter-root.tsx @@ -21,6 +21,7 @@ export const ActivityFilterRoot: FC = (props) => { return { key: filterKey, label: value.label, + labelTranslationKey: value.labelTranslationKey, isSelected: selectedFilters.includes(filterKey), onClick: () => toggleFilter(filterKey), }; diff --git a/web/ce/constants/issues.ts b/web/ce/constants/issues.ts index 70a34577f73..33e3f8a2d55 100644 --- a/web/ce/constants/issues.ts +++ b/web/ce/constants/issues.ts @@ -7,20 +7,24 @@ export enum EActivityFilterType { export type TActivityFilters = EActivityFilterType; -export const ACTIVITY_FILTER_TYPE_OPTIONS: Record = { - [EActivityFilterType.ACTIVITY]: { - label: "Updates", - }, - [EActivityFilterType.COMMENT]: { - label: "Comments", - }, -}; +export const ACTIVITY_FILTER_TYPE_OPTIONS: Record = + { + [EActivityFilterType.ACTIVITY]: { + label: "Updates", + labelTranslationKey: "updates", + }, + [EActivityFilterType.COMMENT]: { + label: "Comments", + labelTranslationKey: "comments", + }, + }; export const defaultActivityFilters: TActivityFilters[] = [EActivityFilterType.ACTIVITY, EActivityFilterType.COMMENT]; export type TActivityFilterOption = { key: TActivityFilters; label: string; + labelTranslationKey: string; isSelected: boolean; onClick: () => void; }; diff --git a/web/core/components/core/filters/date-filter-modal.tsx b/web/core/components/core/filters/date-filter-modal.tsx index 9fa63ad6450..a5509dd63e5 100644 --- a/web/core/components/core/filters/date-filter-modal.tsx +++ b/web/core/components/core/filters/date-filter-modal.tsx @@ -7,6 +7,7 @@ import { Controller, useForm } from "react-hook-form"; import { X } from "lucide-react"; import { Dialog, Transition } from "@headlessui/react"; +import { useTranslation } from "@plane/i18n"; import { Button } from "@plane/ui"; import { renderFormattedPayloadDate, renderFormattedDate, getDate } from "@/helpers/date-time.helper"; @@ -32,6 +33,7 @@ const defaultValues: TFormValues = { }; export const DateFilterModal: React.FC = ({ title, handleClose, isOpen, onSelect }) => { + const { t } = useTranslation(); const { handleSubmit, watch, control } = useForm({ defaultValues, }); @@ -136,15 +138,15 @@ export const DateFilterModal: React.FC = ({ title, handleClose, isOpen, o
{watch("filterType") === "range" && (
- After: + {t("after")}: {renderFormattedDate(watch("date1"))} - Before: + {t("before")}: {!isInvalid && {renderFormattedDate(watch("date2"))}}
)}
diff --git a/web/core/components/core/filters/date-filter-select.tsx b/web/core/components/core/filters/date-filter-select.tsx index 45c38c1c309..064c215c1e8 100644 --- a/web/core/components/core/filters/date-filter-select.tsx +++ b/web/core/components/core/filters/date-filter-select.tsx @@ -2,6 +2,7 @@ import React from "react"; import { CalendarDays } from "lucide-react"; // ui +import { useTranslation } from "@plane/i18n"; import { CustomSelect, CalendarAfterIcon, CalendarBeforeIcon } from "@plane/ui"; type Props = { @@ -11,6 +12,7 @@ type Props = { }; type DueDate = { + nameTranslationKey: string; name: string; value: string; icon: any; @@ -18,30 +20,34 @@ type DueDate = { const dueDateRange: DueDate[] = [ { + nameTranslationKey: "before", name: "before", value: "before", icon: , }, { + nameTranslationKey: "after", name: "after", value: "after", icon: , }, { + nameTranslationKey: "range", name: "range", value: "range", icon: , }, ]; -export const DateFilterSelect: React.FC = ({ title, value, onChange }) => ( - = ({ title, value, onChange }) => { + const { t } = useTranslation(); + return {dueDateRange.find((item) => item.value === value)?.icon} - {title} {dueDateRange.find((item) => item.value === value)?.name} + {title} {t(dueDateRange.find((item) => item.value === value)?.nameTranslationKey || "")} } @@ -51,9 +57,9 @@ export const DateFilterSelect: React.FC = ({ title, value, onChange }) =>
{option.icon} - {title} {option.name} + {title} {t(option.nameTranslationKey)}
))}
-); +}; diff --git a/web/core/components/dropdowns/cycle/cycle-options.tsx b/web/core/components/dropdowns/cycle/cycle-options.tsx index e720fb78c9f..a0d81aba3b3 100644 --- a/web/core/components/dropdowns/cycle/cycle-options.tsx +++ b/web/core/components/dropdowns/cycle/cycle-options.tsx @@ -8,6 +8,8 @@ import { usePopper } from "react-popper"; // components import { Check, Search } from "lucide-react"; import { Combobox } from "@headlessui/react"; +// i18n +import { useTranslation } from "@plane/i18n"; // icon import { TCycleGroups } from "@plane/types"; // ui @@ -36,6 +38,8 @@ type CycleOptionsProps = { export const CycleOptions: FC = observer((props) => { const { projectId, isOpen, referenceElement, placement, canRemoveCycle, currentCycleId } = props; + // i18n + const { t } = useTranslation(); //state hooks const [query, setQuery] = useState(""); const [popperElement, setPopperElement] = useState(null); @@ -103,11 +107,11 @@ export const CycleOptions: FC = observer((props) => { if (canRemoveCycle) { options?.unshift({ value: null, - query: "No cycle", + query: t("no_cycle"), content: (
- No cycle + {t("no_cycle")}
), }); @@ -132,7 +136,7 @@ export const CycleOptions: FC = observer((props) => { className="w-full bg-transparent py-1 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none" value={query} onChange={(e) => setQuery(e.target.value)} - placeholder="Search" + placeholder={t("search")} displayValue={(assigned: any) => assigned?.name} onKeyDown={searchInputKeyDown} /> @@ -159,10 +163,10 @@ export const CycleOptions: FC = observer((props) => { )) ) : ( -

No matches found

+

{t("no_matches_found")}

) ) : ( -

Loading...

+

{t("loading")}

)} diff --git a/web/core/components/dropdowns/cycle/index.tsx b/web/core/components/dropdowns/cycle/index.tsx index 98124a0a19d..b66b7987120 100644 --- a/web/core/components/dropdowns/cycle/index.tsx +++ b/web/core/components/dropdowns/cycle/index.tsx @@ -3,6 +3,7 @@ import { ReactNode, useRef, useState } from "react"; import { observer } from "mobx-react"; import { ChevronDown } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; // ui import { ComboDropDown, ContrastIcon } from "@plane/ui"; // helpers @@ -52,8 +53,9 @@ export const CycleDropdown: React.FC = observer((props) => { renderByDefault = true, currentCycleId, } = props; + // i18n + const { t } = useTranslation(); // states - const [isOpen, setIsOpen] = useState(false); const { getCycleNameById } = useCycle(); // refs @@ -108,7 +110,7 @@ export const CycleDropdown: React.FC = observer((props) => { = (props) => { // store hooks const { getModuleById } = useModule(); const { isMobile } = usePlatformOS(); + // i18n + const { t } = useTranslation(); if (Array.isArray(value)) return ( @@ -84,7 +87,7 @@ const ButtonContent: React.FC = (props) => { {value.length > 0 ? value.length === 1 ? `${getModuleById(value[0])?.name || "module"}` - : `${value.length} Module${value.length === 1 ? "" : "s"}` + : `${value.length} ${value.length === 1 ? t("module") : t("modules")}` : placeholder} )} @@ -104,7 +107,7 @@ const ButtonContent: React.FC = (props) => { {!hideIcon && } {!hideText && ( = (props) => { )} {!disabled && ( = observer((props) => { // store hooks const { isMobile } = usePlatformOS(); + // i18n + const { t } = useTranslation(); + const { getModuleNameById } = useModule(); const { handleClose, handleKeyDown, handleOnClick } = useDropdown({ @@ -256,7 +262,7 @@ export const ModuleDropdown: React.FC = observer((props) => { { const { projectId, isOpen, referenceElement, placement, multiple } = props; + // i18n + const { t } = useTranslation(); // states const [query, setQuery] = useState(""); const [popperElement, setPopperElement] = useState(null); @@ -97,11 +100,11 @@ export const ModuleOptions = observer((props: Props) => { if (!multiple) options?.unshift({ value: null, - query: "No module", + query: t("no_module"), content: (
- No module + {t("no_module")}
), }); @@ -125,7 +128,7 @@ export const ModuleOptions = observer((props: Props) => { className="w-full bg-transparent py-1 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none" value={query} onChange={(e) => setQuery(e.target.value)} - placeholder="Search" + placeholder={t("search")} displayValue={(assigned: any) => assigned?.name} onKeyDown={searchInputKeyDown} /> @@ -157,10 +160,10 @@ export const ModuleOptions = observer((props: Props) => { )) ) : ( -

No matching results

+

{t("no_matches_found")}

) ) : ( -

Loading...

+

{t("loading")}

)} diff --git a/web/core/components/dropdowns/state.tsx b/web/core/components/dropdowns/state.tsx index 59178c14968..fbb2ed368a5 100644 --- a/web/core/components/dropdowns/state.tsx +++ b/web/core/components/dropdowns/state.tsx @@ -222,7 +222,7 @@ export const StateDropdown: React.FC = observer((props) => { className="w-full bg-transparent py-1 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none" value={query} onChange={(e) => setQuery(e.target.value)} - placeholder="Search" + placeholder={t("search")} displayValue={(assigned: any) => assigned?.name} onKeyDown={searchInputKeyDown} /> diff --git a/web/core/components/issues/create-issue-toast-action-items.tsx b/web/core/components/issues/create-issue-toast-action-items.tsx index aff00f00031..ad8635d6c56 100644 --- a/web/core/components/issues/create-issue-toast-action-items.tsx +++ b/web/core/components/issues/create-issue-toast-action-items.tsx @@ -2,6 +2,7 @@ import React, { FC, useState } from "react"; import { observer } from "mobx-react"; // helpers +import { useTranslation } from "@plane/i18n"; import { copyUrlToClipboard } from "@/helpers/string.helper"; // hooks import { useIssueDetail } from "@/hooks/store"; @@ -15,6 +16,7 @@ type TCreateIssueToastActionItems = { export const CreateIssueToastActionItems: FC = observer((props) => { const { workspaceSlug, projectId, issueId, isEpic = false } = props; + const { t } = useTranslation(); // state const [copied, setCopied] = useState(false); // store hooks @@ -49,12 +51,12 @@ export const CreateIssueToastActionItems: FC = obs rel="noopener noreferrer" className="text-custom-primary px-2 py-1 hover:bg-custom-background-90 font-medium rounded" > - {`View ${isEpic ? "epic" : "issue"}`} + {`${t("view")} ${isEpic ? t("epic") : t("issue")}`} {copied ? ( <> - Copied! + {t("copied")} ) : ( <> @@ -62,7 +64,7 @@ export const CreateIssueToastActionItems: FC = obs className="cursor-pointer hidden group-hover:flex px-2 py-1 text-custom-text-300 hover:text-custom-text-200 hover:bg-custom-background-90 rounded" onClick={copyToClipboard} > - Copy link + {t("copy_link")} )} diff --git a/web/core/components/issues/filters.tsx b/web/core/components/issues/filters.tsx index e09b5434857..6a52e689e7e 100644 --- a/web/core/components/issues/filters.tsx +++ b/web/core/components/issues/filters.tsx @@ -5,6 +5,7 @@ import { observer } from "mobx-react"; // plane constants import { EIssueLayoutTypes, EIssueFilterType, EIssuesStoreType } from "@plane/constants"; // types +import { useTranslation } from "@plane/i18n"; import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions } from "@plane/types"; import { Button } from "@plane/ui"; // components @@ -34,6 +35,7 @@ const HeaderFilters = observer((props: Props) => { canUserCreateIssue, storeType = EIssuesStoreType.PROJECT, } = props; + const { t } = useTranslation(); // states const [analyticsModal, setAnalyticsModal] = useState(false); // store hooks @@ -111,7 +113,7 @@ const HeaderFilters = observer((props: Props) => { onChange={(layout) => handleLayoutChange(layout)} selectedLayout={activeLayout} /> - + { isEpic={storeType === EIssuesStoreType.EPIC} /> - + { {canUserCreateIssue ? ( ) : ( <> diff --git a/web/core/components/issues/issue-detail/issue-activity/activity-filter.tsx b/web/core/components/issues/issue-detail/issue-activity/activity-filter.tsx index 3733e7f72b2..c8d8f982649 100644 --- a/web/core/components/issues/issue-detail/issue-activity/activity-filter.tsx +++ b/web/core/components/issues/issue-detail/issue-activity/activity-filter.tsx @@ -1,6 +1,7 @@ import React, { FC } from "react"; import { observer } from "mobx-react"; import { Check, ListFilter } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; import { Button, PopoverMenu } from "@plane/ui"; // helper import { cn } from "@/helpers/common.helper"; @@ -14,6 +15,7 @@ type TActivityFilter = { export const ActivityFilter: FC = observer((props) => { const { selectedFilters = [], filterOptions } = props; + const { t } = useTranslation(); return ( = observer((props) => { prependIcon={} className="relative" > - Filters + {t("filters")} {selectedFilters.length < filterOptions.length && ( )} @@ -53,7 +55,7 @@ export const ActivityFilter: FC = observer((props) => { {item.isSelected && }
- {item.label} + {t(item.labelTranslationKey)}
)} diff --git a/web/core/components/issues/issue-detail/issue-activity/root.tsx b/web/core/components/issues/issue-detail/issue-activity/root.tsx index 90bc93f1c0e..6a06422bce6 100644 --- a/web/core/components/issues/issue-detail/issue-activity/root.tsx +++ b/web/core/components/issues/issue-detail/issue-activity/root.tsx @@ -5,6 +5,7 @@ import { observer } from "mobx-react"; // plane package imports import { E_SORT_ORDER } from "@plane/constants"; import { useLocalStorage } from "@plane/hooks"; +import { useTranslation } from "@plane/i18n"; import { TFileSignedURLResponse, TIssueComment } from "@plane/types"; import { EFileAssetType } from "@plane/types/src/enums"; import { TOAST_TYPE, setToast } from "@plane/ui"; @@ -40,6 +41,8 @@ export type TActivityOperations = { export const IssueActivity: FC = observer((props) => { const { workspaceSlug, projectId, issueId, disabled = false, isIntakeIssue = false } = props; + // i18n + const { t } = useTranslation(); // hooks const { setValue: setFilterValue, storedValue: selectedFilters } = useLocalStorage( "issue_activity_filters", @@ -84,59 +87,59 @@ export const IssueActivity: FC = observer((props) => { () => ({ createComment: async (data) => { try { - if (!workspaceSlug || !projectId || !issueId) throw new Error("Missing fields"); + if (!workspaceSlug || !projectId || !issueId) throw new Error(t("missing_fields")); const comment = await createComment(workspaceSlug, projectId, issueId, data); setToast({ - title: "Success!", + title: t("success"), type: TOAST_TYPE.SUCCESS, - message: "Comment created successfully.", + message: t("comment_created_successfully"), }); return comment; } catch (error) { setToast({ - title: "Error!", + title: t("error"), type: TOAST_TYPE.ERROR, - message: "Comment creation failed. Please try again later.", + message: t("comment_creation_failed_please_try_again_later"), }); } }, updateComment: async (commentId, data) => { try { - if (!workspaceSlug || !projectId || !issueId) throw new Error("Missing fields"); + if (!workspaceSlug || !projectId || !issueId) throw new Error(t("missing_fields")); await updateComment(workspaceSlug, projectId, issueId, commentId, data); setToast({ - title: "Success!", + title: t("success"), type: TOAST_TYPE.SUCCESS, - message: "Comment updated successfully.", + message: t("comment_updated_successfully"), }); } catch (error) { setToast({ - title: "Error!", + title: t("error"), type: TOAST_TYPE.ERROR, - message: "Comment update failed. Please try again later.", + message: t("comment_update_failed_please_try_again_later"), }); } }, removeComment: async (commentId) => { try { - if (!workspaceSlug || !projectId || !issueId) throw new Error("Missing fields"); + if (!workspaceSlug || !projectId || !issueId) throw new Error(t("missing_fields")); await removeComment(workspaceSlug, projectId, issueId, commentId); setToast({ - title: "Success!", + title: t("success"), type: TOAST_TYPE.SUCCESS, - message: "Comment removed successfully.", + message: t("comment_removed_successfully"), }); } catch (error) { setToast({ - title: "Error!", + title: t("error"), type: TOAST_TYPE.ERROR, - message: "Comment remove failed. Please try again later.", + message: t("comment_remove_failed_please_try_again_later"), }); } }, uploadCommentAsset: async (file, commentId) => { try { - if (!workspaceSlug || !projectId) throw new Error("Missing fields"); + if (!workspaceSlug || !projectId) throw new Error(t("missing_fields")); const res = await fileService.uploadProjectAsset( workspaceSlug, projectId, @@ -149,7 +152,7 @@ export const IssueActivity: FC = observer((props) => { return res; } catch (error) { console.log("Error in uploading comment asset:", error); - throw new Error("Asset upload failed. Please try again later."); + throw new Error(t("asset_upload_failed_please_try_again_later")); } }, }), @@ -163,7 +166,7 @@ export const IssueActivity: FC = observer((props) => {
{/* header */}
-
Activity
+
{t("activity")}
{isWorklogButtonEnabled && ( = observer((props) => { alwaysAllowEditing, disableEditing = false, } = props; + + const { t } = useTranslation(); + // store hooks const { allowPermissions } = useUserPermissions(); @@ -156,7 +160,7 @@ export const AppliedFiltersList: React.FC = observer((props) => { {isEditingAllowed && ( diff --git a/web/core/components/issues/issue-layouts/filters/header/display-filters/display-properties.tsx b/web/core/components/issues/issue-layouts/filters/header/display-filters/display-properties.tsx index 9651343492c..74a6e854363 100644 --- a/web/core/components/issues/issue-layouts/filters/header/display-filters/display-properties.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/display-filters/display-properties.tsx @@ -2,6 +2,7 @@ import React from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // types +import { useTranslation } from "@plane/i18n"; import { IIssueDisplayProperties } from "@plane/types"; // constants import { ISSUE_DISPLAY_PROPERTIES } from "@/constants/issue"; @@ -28,6 +29,7 @@ export const FilterDisplayProperties: React.FC = observer((props) => { moduleViewDisabled = false, isEpic = false, } = props; + const { t } = useTranslation(); // router const { workspaceSlug, projectId: routerProjectId } = useParams(); // states @@ -57,7 +59,7 @@ export const FilterDisplayProperties: React.FC = observer((props) => { return ( <> setPreviewEnabled(!previewEnabled)} /> @@ -79,7 +81,7 @@ export const FilterDisplayProperties: React.FC = observer((props) => { }) } > - {displayProperty.title} + {t(displayProperty.titleTranslationKey)} ))} diff --git a/web/core/components/issues/issue-layouts/filters/header/display-filters/extra-options.tsx b/web/core/components/issues/issue-layouts/filters/header/display-filters/extra-options.tsx index a5067377d17..0dcd5c1ecbd 100644 --- a/web/core/components/issues/issue-layouts/filters/header/display-filters/extra-options.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/display-filters/extra-options.tsx @@ -1,7 +1,7 @@ import React from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { IIssueDisplayFilterOptions, TIssueExtraOptions } from "@plane/types"; - // components import { FilterOption } from "@/components/issues"; // types @@ -20,6 +20,8 @@ type Props = { export const FilterExtraOptions: React.FC = observer((props) => { const { selectedExtraOptions, handleUpdate, enabledExtraOptions } = props; + const { t } = useTranslation(); + const isExtraOptionEnabled = (option: TIssueExtraOptions) => enabledExtraOptions.includes(option); return ( @@ -32,7 +34,7 @@ export const FilterExtraOptions: React.FC = observer((props) => { key={option.key} isChecked={selectedExtraOptions?.[option.key] ? true : false} onClick={() => handleUpdate(option.key, !selectedExtraOptions?.[option.key])} - title={option.title} + title={t(option.titleTranslationKey)} /> ); })} diff --git a/web/core/components/issues/issue-layouts/filters/header/display-filters/group-by.tsx b/web/core/components/issues/issue-layouts/filters/header/display-filters/group-by.tsx index c5adff75b9c..45726927f12 100644 --- a/web/core/components/issues/issue-layouts/filters/header/display-filters/group-by.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/display-filters/group-by.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { IIssueDisplayFilterOptions, TIssueGroupByOptions } from "@plane/types"; // components import { FilterHeader, FilterOption } from "@/components/issues"; @@ -17,6 +18,8 @@ type Props = { export const FilterGroupBy: React.FC = observer((props) => { const { displayFilters, groupByOptions, handleUpdate, ignoreGroupedFilters } = props; + const { t } = useTranslation(); + const [previewEnabled, setPreviewEnabled] = useState(true); const selectedGroupBy = displayFilters?.group_by ?? null; @@ -25,7 +28,7 @@ export const FilterGroupBy: React.FC = observer((props) => { return ( <> setPreviewEnabled(!previewEnabled)} /> @@ -45,7 +48,7 @@ export const FilterGroupBy: React.FC = observer((props) => { key={groupBy?.key} isChecked={selectedGroupBy === groupBy?.key ? true : false} onClick={() => handleUpdate(groupBy.key)} - title={groupBy.title} + title={t(groupBy.titleTranslationKey)} multiple={false} /> ); diff --git a/web/core/components/issues/issue-layouts/filters/header/display-filters/issue-grouping.tsx b/web/core/components/issues/issue-layouts/filters/header/display-filters/issue-grouping.tsx index 0de60f625be..22eca56f0c4 100644 --- a/web/core/components/issues/issue-layouts/filters/header/display-filters/issue-grouping.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/display-filters/issue-grouping.tsx @@ -1,5 +1,6 @@ import React from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { TIssueGroupingFilters } from "@plane/types"; // components @@ -16,6 +17,8 @@ type Props = { export const FilterIssueGrouping: React.FC = observer((props) => { const { selectedIssueType, handleUpdate, isEpic = false } = props; + // i18n + const { t } = useTranslation(); const [previewEnabled, setPreviewEnabled] = React.useState(true); @@ -24,7 +27,7 @@ export const FilterIssueGrouping: React.FC = observer((props) => { return ( <> setPreviewEnabled(!previewEnabled)} /> @@ -35,7 +38,7 @@ export const FilterIssueGrouping: React.FC = observer((props) => { key={issueType?.key} isChecked={activeIssueType === issueType?.key ? true : false} onClick={() => handleUpdate(issueType?.key)} - title={`${issueType.title} ${isEpic ? "Epics" : "Issues"}`} + title={`${t(issueType?.titleTranslationKey)} ${isEpic ? t("epics") : t("issues")}`} multiple={false} /> ))} diff --git a/web/core/components/issues/issue-layouts/filters/header/display-filters/order-by.tsx b/web/core/components/issues/issue-layouts/filters/header/display-filters/order-by.tsx index a9ff00b3d0e..8ebf2b205d5 100644 --- a/web/core/components/issues/issue-layouts/filters/header/display-filters/order-by.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/display-filters/order-by.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { TIssueOrderByOptions } from "@plane/types"; - // components import { FilterHeader, FilterOption } from "@/components/issues"; // types @@ -17,6 +17,8 @@ type Props = { export const FilterOrderBy: React.FC = observer((props) => { const { selectedOrderBy, handleUpdate, orderByOptions } = props; + const { t } = useTranslation(); + const [previewEnabled, setPreviewEnabled] = useState(true); const activeOrderBy = selectedOrderBy ?? "-created_at"; @@ -24,7 +26,7 @@ export const FilterOrderBy: React.FC = observer((props) => { return ( <> setPreviewEnabled(!previewEnabled)} /> @@ -35,7 +37,7 @@ export const FilterOrderBy: React.FC = observer((props) => { key={orderBy?.key} isChecked={activeOrderBy === orderBy?.key ? true : false} onClick={() => handleUpdate(orderBy.key)} - title={orderBy.title} + title={t(orderBy.titleTranslationKey)} multiple={false} /> ))} diff --git a/web/core/components/issues/issue-layouts/filters/header/display-filters/sub-group-by.tsx b/web/core/components/issues/issue-layouts/filters/header/display-filters/sub-group-by.tsx index 56dd86f36e5..7119661c3ae 100644 --- a/web/core/components/issues/issue-layouts/filters/header/display-filters/sub-group-by.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/display-filters/sub-group-by.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { IIssueDisplayFilterOptions, TIssueGroupByOptions } from "@plane/types"; // components import { FilterHeader, FilterOption } from "@/components/issues"; @@ -17,6 +18,8 @@ type Props = { export const FilterSubGroupBy: React.FC = observer((props) => { const { displayFilters, handleUpdate, subGroupByOptions, ignoreGroupedFilters } = props; + const { t } = useTranslation(); + const [previewEnabled, setPreviewEnabled] = useState(true); const selectedGroupBy = displayFilters.group_by ?? null; @@ -25,7 +28,7 @@ export const FilterSubGroupBy: React.FC = observer((props) => { return ( <> setPreviewEnabled(!previewEnabled)} /> diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/assignee.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/assignee.tsx index 553fd7d4184..a6bd1d9fcbd 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/assignee.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/assignee.tsx @@ -4,6 +4,7 @@ import { useMemo, useState } from "react"; import sortBy from "lodash/sortBy"; import { observer } from "mobx-react"; // plane ui +import { useTranslation } from "@plane/i18n"; import { Avatar, Loader } from "@plane/ui"; // components import { FilterHeader, FilterOption } from "@/components/issues"; @@ -21,6 +22,8 @@ type Props = { export const FilterAssignees: React.FC = observer((props: Props) => { const { appliedFilters, handleUpdate, memberIds, searchQuery } = props; + // i18n + const { t } = useTranslation(); // states const [itemsToRender, setItemsToRender] = useState(5); const [previewEnabled, setPreviewEnabled] = useState(true); @@ -53,7 +56,7 @@ export const FilterAssignees: React.FC = observer((props: Props) => { return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("assignee")}${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -79,7 +82,7 @@ export const FilterAssignees: React.FC = observer((props: Props) => { size="md" /> } - title={currentUser?.id === member.id ? "You" : member?.display_name} + title={currentUser?.id === member.id ? t("you") : member?.display_name} /> ); })} @@ -94,7 +97,7 @@ export const FilterAssignees: React.FC = observer((props: Props) => { )} ) : ( -

No matches found

+

{t("no_matches_found")}

) ) : ( diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/created-by.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/created-by.tsx index 2fbcf6233cc..283589fad0d 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/created-by.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/created-by.tsx @@ -4,6 +4,7 @@ import { useMemo, useState } from "react"; import sortBy from "lodash/sortBy"; import { observer } from "mobx-react"; // plane ui +import { useTranslation } from "@plane/i18n"; import { Avatar, Loader } from "@plane/ui"; // components import { FilterHeader, FilterOption } from "@/components/issues"; @@ -21,6 +22,8 @@ type Props = { export const FilterCreatedBy: React.FC = observer((props: Props) => { const { appliedFilters, handleUpdate, memberIds, searchQuery } = props; + // i18n + const { t } = useTranslation(); // states const [itemsToRender, setItemsToRender] = useState(5); const [previewEnabled, setPreviewEnabled] = useState(true); @@ -53,7 +56,7 @@ export const FilterCreatedBy: React.FC = observer((props: Props) => { return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("created_by")}${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -72,7 +75,7 @@ export const FilterCreatedBy: React.FC = observer((props: Props) => { isChecked={appliedFilters?.includes(member.id) ? true : false} onClick={() => handleUpdate(member.id)} icon={} - title={currentUser?.id === member.id ? "You" : member?.display_name} + title={currentUser?.id === member.id ? t("you") : member?.display_name} /> ); })} @@ -82,12 +85,12 @@ export const FilterCreatedBy: React.FC = observer((props: Props) => { className="ml-8 text-xs font-medium text-custom-primary-100" onClick={handleViewToggle} > - {itemsToRender === sortedOptions.length ? "View less" : "View all"} + {itemsToRender === sortedOptions.length ? t("view_less") : t("view_all")} )} ) : ( -

No matches found

+

{t("no_matches_found")}

) ) : ( diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/cycle.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/cycle.tsx index 8169b43c8b0..94d378ecadb 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/cycle.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/cycle.tsx @@ -4,6 +4,7 @@ import React, { useMemo, useState } from "react"; import sortBy from "lodash/sortBy"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; +import { useTranslation } from "@plane/i18n"; import { TCycleGroups } from "@plane/types"; // components import { Loader, CycleGroupIcon } from "@plane/ui"; @@ -20,7 +21,8 @@ type Props = { export const FilterCycle: React.FC = observer((props) => { const { appliedFilters, handleUpdate, searchQuery } = props; - + // i18n + const { t } = useTranslation(); // hooks const { projectId } = useParams(); const { getCycleById, getProjectCycleIds } = useCycle(); @@ -58,7 +60,7 @@ export const FilterCycle: React.FC = observer((props) => { return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("cycle")} ${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -85,12 +87,12 @@ export const FilterCycle: React.FC = observer((props) => { className="ml-8 text-xs font-medium text-custom-primary-100" onClick={handleViewToggle} > - {itemsToRender === sortedOptions.length ? "View less" : "View all"} + {itemsToRender === sortedOptions.length ? t("view_less") : t("view_all")} )} ) : ( -

No matches found

+

{t("no_matches_found")}

) ) : ( diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx index 37eee9e936f..fab6a43ebd6 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { Search, X } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; // types import { IIssueDisplayFilterOptions, @@ -63,6 +64,8 @@ export const FilterSelection: React.FC = observer((props) => { moduleViewDisabled = false, isEpic = false, } = props; + // i18n + const { t } = useTranslation(); // hooks const { isMobile } = usePlatformOS(); const { moduleId, cycleId } = useParams(); @@ -95,7 +98,7 @@ export const FilterSelection: React.FC = observer((props) => { setFiltersSearchQuery(e.target.value)} autoFocus={!isMobile} diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/labels.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/labels.tsx index 70800af0e80..0c6ecb86b8a 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/labels.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/labels.tsx @@ -3,6 +3,7 @@ import React, { useMemo, useState } from "react"; import sortBy from "lodash/sortBy"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { IIssueLabel } from "@plane/types"; // components import { Loader } from "@plane/ui"; @@ -22,6 +23,9 @@ type Props = { }; export const FilterLabels: React.FC = observer((props) => { + // i18n + const { t } = useTranslation(); + const { appliedFilters, handleUpdate, labels, searchQuery } = props; const [itemsToRender, setItemsToRender] = useState(5); @@ -51,7 +55,7 @@ export const FilterLabels: React.FC = observer((props) => { return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("label")}${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -75,12 +79,12 @@ export const FilterLabels: React.FC = observer((props) => { className="ml-8 text-xs font-medium text-custom-primary-100" onClick={handleViewToggle} > - {itemsToRender === sortedOptions.length ? "View less" : "View all"} + {itemsToRender === sortedOptions.length ? t("view_less") : t("view_all")} )} ) : ( -

No matches found

+

{t("no_matches_found")}

) ) : ( diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/mentions.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/mentions.tsx index e38f5794d8d..80e5f4ec75f 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/mentions.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/mentions.tsx @@ -4,6 +4,7 @@ import { useMemo, useState } from "react"; import sortBy from "lodash/sortBy"; import { observer } from "mobx-react"; // plane ui +import { useTranslation } from "@plane/i18n"; import { Loader, Avatar } from "@plane/ui"; // components import { FilterHeader, FilterOption } from "@/components/issues"; @@ -20,6 +21,8 @@ type Props = { }; export const FilterMentions: React.FC = observer((props: Props) => { + // i18n + const { t } = useTranslation(); const { appliedFilters, handleUpdate, memberIds, searchQuery } = props; // states const [itemsToRender, setItemsToRender] = useState(5); @@ -53,7 +56,7 @@ export const FilterMentions: React.FC = observer((props: Props) => { return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("mention")}${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -79,7 +82,7 @@ export const FilterMentions: React.FC = observer((props: Props) => { size={"md"} /> } - title={currentUser?.id === member.id ? "You" : member?.display_name} + title={currentUser?.id === member.id ? t("you") : member?.display_name} /> ); })} @@ -89,12 +92,12 @@ export const FilterMentions: React.FC = observer((props: Props) => { className="ml-8 text-xs font-medium text-custom-primary-100" onClick={handleViewToggle} > - {itemsToRender === sortedOptions.length ? "View less" : "View all"} + {itemsToRender === sortedOptions.length ? t("view_less") : t("view_all")} )} ) : ( -

No matches found

+

{t("no_matches_found")}

) ) : ( diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/module.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/module.tsx index ea972db0325..70e79128803 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/module.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/module.tsx @@ -5,6 +5,7 @@ import sortBy from "lodash/sortBy"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // components +import { useTranslation } from "@plane/i18n"; import { Loader, DiceIcon } from "@plane/ui"; import { FilterHeader, FilterOption } from "@/components/issues"; import { useModule } from "@/hooks/store"; @@ -18,6 +19,8 @@ type Props = { export const FilterModule: React.FC = observer((props) => { const { appliedFilters, handleUpdate, searchQuery } = props; + // i18n + const { t } = useTranslation(); // hooks const { projectId } = useParams(); const { getModuleById, getProjectModuleIds } = useModule(); @@ -51,7 +54,7 @@ export const FilterModule: React.FC = observer((props) => { return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("module")} ${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -75,12 +78,12 @@ export const FilterModule: React.FC = observer((props) => { className="ml-8 text-xs font-medium text-custom-primary-100" onClick={handleViewToggle} > - {itemsToRender === sortedOptions.length ? "View less" : "View all"} + {itemsToRender === sortedOptions.length ? t("view_less") : t("view_all")} )} ) : ( -

No matches found

+

{t("no_matches_found")}

) ) : ( diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/priority.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/priority.tsx index 609bb37f473..1a292198524 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/priority.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/priority.tsx @@ -3,6 +3,7 @@ import React, { useState } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; // ui import { PriorityIcon } from "@plane/ui"; @@ -21,6 +22,9 @@ type Props = { export const FilterPriority: React.FC = observer((props) => { const { appliedFilters, handleUpdate, searchQuery } = props; + // i18n + const { t } = useTranslation(); + const [previewEnabled, setPreviewEnabled] = useState(true); const appliedFiltersCount = appliedFilters?.length ?? 0; @@ -30,7 +34,7 @@ export const FilterPriority: React.FC = observer((props) => { return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("priority")} ${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -43,11 +47,11 @@ export const FilterPriority: React.FC = observer((props) => { isChecked={appliedFilters?.includes(priority.key) ? true : false} onClick={() => handleUpdate(priority.key)} icon={} - title={priority.title} + title={t(priority.titleTranslationKey)} /> )) ) : ( -

No matches found

+

{t("no_matches_found")}

)}
)} diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/project.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/project.tsx index 02fac50a3b6..d3ac868e37c 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/project.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/project.tsx @@ -3,6 +3,7 @@ import React, { useMemo, useState } from "react"; import sortBy from "lodash/sortBy"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; // ui import { Loader } from "@plane/ui"; // components @@ -19,6 +20,8 @@ type Props = { export const FilterProjects: React.FC = observer((props) => { const { appliedFilters, handleUpdate, searchQuery } = props; + // i18n + const { t } = useTranslation(); // states const [itemsToRender, setItemsToRender] = useState(5); const [previewEnabled, setPreviewEnabled] = useState(true); @@ -49,7 +52,7 @@ export const FilterProjects: React.FC = observer((props) => { return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("project")}${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -77,12 +80,12 @@ export const FilterProjects: React.FC = observer((props) => { className="ml-8 text-xs font-medium text-custom-primary-100" onClick={handleViewToggle} > - {itemsToRender === sortedOptions.length ? "View less" : "View all"} + {itemsToRender === sortedOptions.length ? t("view_less") : t("view_all")} )} ) : ( -

No matches found

+

{t("no_matches_found")}

) ) : ( diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/start-date.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/start-date.tsx index c9f4ae22e65..c9766cebf09 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/start-date.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/start-date.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; // components import { DateFilterModal } from "@/components/core"; import { FilterHeader, FilterOption } from "@/components/issues"; @@ -15,7 +16,8 @@ type Props = { export const FilterStartDate: React.FC = observer((props) => { const { appliedFilters, handleUpdate, searchQuery } = props; - + // i18n + const { t } = useTranslation(); const [previewEnabled, setPreviewEnabled] = useState(true); const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false); @@ -43,11 +45,11 @@ export const FilterStartDate: React.FC = observer((props) => { handleClose={() => setIsDateFilterModalOpen(false)} isOpen={isDateFilterModalOpen} onSelect={(val) => handleUpdate(val)} - title="Start date" + title={t("start_date")} /> )} 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("start_date")}${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -60,14 +62,14 @@ export const FilterStartDate: React.FC = observer((props) => { key={option.value} isChecked={appliedFilters?.includes(option.value) ? true : false} onClick={() => handleUpdate(option.value)} - title={option.name} + title={t(option.nameTranslationKey)} multiple /> ))} - + ) : ( -

No matches found

+

{t("no_matches_found")}

)}
)} diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/state-group.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/state-group.tsx index 1ca11eec629..2667b972502 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/state-group.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/state-group.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; // components import { StateGroupIcon } from "@plane/ui"; import { FilterHeader, FilterOption } from "@/components/issues"; @@ -16,6 +17,9 @@ type Props = { }; export const FilterStateGroup: React.FC = observer((props) => { + // i18n + const { t } = useTranslation(); + const { appliedFilters, handleUpdate, searchQuery } = props; const [itemsToRender, setItemsToRender] = useState(5); @@ -35,7 +39,7 @@ export const FilterStateGroup: React.FC = observer((props) => { return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("state_group")} ${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -58,12 +62,12 @@ export const FilterStateGroup: React.FC = observer((props) => { className="ml-8 text-xs font-medium text-custom-primary-100" onClick={handleViewToggle} > - {itemsToRender === filteredOptions.length ? "View less" : "View all"} + {itemsToRender === filteredOptions.length ? t("view_less") : t("view_all")} )} ) : ( -

No matches found

+

{t("no_matches_found")}

)}
)} diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/state.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/state.tsx index aeffcee4144..9223efba3a6 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/state.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/state.tsx @@ -3,6 +3,7 @@ import React, { useMemo, useState } from "react"; import sortBy from "lodash/sortBy"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { IState } from "@plane/types"; // components import { Loader, StateGroupIcon } from "@plane/ui"; @@ -19,6 +20,8 @@ type Props = { export const FilterState: React.FC = observer((props) => { const { appliedFilters, handleUpdate, searchQuery, states } = props; + // i18n + const { t } = useTranslation(); const [itemsToRender, setItemsToRender] = useState(5); const [previewEnabled, setPreviewEnabled] = useState(true); @@ -42,7 +45,7 @@ export const FilterState: React.FC = observer((props) => { return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("state")}${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -66,12 +69,12 @@ export const FilterState: React.FC = observer((props) => { className="ml-8 text-xs font-medium text-custom-primary-100" onClick={handleViewToggle} > - {itemsToRender === sortedOptions.length ? "View less" : "View all"} + {itemsToRender === sortedOptions.length ? t("view_less") : t("view_all")} )} ) : ( -

No matches found

+

{t("no_matches_found")}

) ) : ( diff --git a/web/core/components/issues/issue-layouts/filters/header/filters/target-date.tsx b/web/core/components/issues/issue-layouts/filters/header/filters/target-date.tsx index d4ef89584e4..37bdc0475b7 100644 --- a/web/core/components/issues/issue-layouts/filters/header/filters/target-date.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/filters/target-date.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { observer } from "mobx-react"; - +import { useTranslation } from "@plane/i18n"; // components import { DateFilterModal } from "@/components/core"; import { FilterHeader, FilterOption } from "@/components/issues"; @@ -15,6 +15,8 @@ type Props = { export const FilterTargetDate: React.FC = observer((props) => { const { appliedFilters, handleUpdate, searchQuery } = props; + // i18n + const { t } = useTranslation(); const [previewEnabled, setPreviewEnabled] = useState(true); const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false); @@ -43,11 +45,11 @@ export const FilterTargetDate: React.FC = observer((props) => { handleClose={() => setIsDateFilterModalOpen(false)} isOpen={isDateFilterModalOpen} onSelect={(val) => handleUpdate(val)} - title="Due date" + title={t("target_date")} /> )} 0 ? ` (${appliedFiltersCount})` : ""}`} + title={`${t("target_date")}${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -60,14 +62,14 @@ export const FilterTargetDate: React.FC = observer((props) => { key={option.value} isChecked={appliedFilters?.includes(option.value) ? true : false} onClick={() => handleUpdate(option.value)} - title={option.name} + title={t(option.nameTranslationKey)} multiple /> ))} - + ) : ( -

No matches found

+

{t("no_matches_found")}

)} )} diff --git a/web/core/components/issues/issue-layouts/filters/header/layout-selection.tsx b/web/core/components/issues/issue-layouts/filters/header/layout-selection.tsx index 9439eea5dcf..daf2821cf74 100644 --- a/web/core/components/issues/issue-layouts/filters/header/layout-selection.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/layout-selection.tsx @@ -4,6 +4,7 @@ import React from "react"; // plane constants import { EIssueLayoutTypes } from "@plane/constants"; // ui +import { useTranslation } from "@plane/i18n"; import { Tooltip } from "@plane/ui"; // types // constants @@ -20,6 +21,7 @@ type Props = { export const LayoutSelection: React.FC = (props) => { const { layouts, onChange, selectedLayout } = props; const { isMobile } = usePlatformOS(); + const { t } = useTranslation(); const handleOnChange = (layoutKey: EIssueLayoutTypes) => { if (selectedLayout !== layoutKey) { @@ -30,7 +32,7 @@ export const LayoutSelection: React.FC = (props) => { return (
{ISSUE_LAYOUTS.filter((l) => layouts.includes(l.key)).map((layout) => ( - + ); diff --git a/web/core/components/issues/issue-layouts/quick-add/button/kanban.tsx b/web/core/components/issues/issue-layouts/quick-add/button/kanban.tsx index 918ef33120e..11052b138bf 100644 --- a/web/core/components/issues/issue-layouts/quick-add/button/kanban.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/button/kanban.tsx @@ -1,18 +1,19 @@ import { FC } from "react"; import { observer } from "mobx-react"; import { PlusIcon } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; import { TQuickAddIssueButton } from "../root"; export const KanbanQuickAddIssueButton: FC = observer((props) => { const { onClick, isEpic = false } = props; - + const { t } = useTranslation(); return (
- {`New ${isEpic ? "Epic" : "Issue"}`} + {`${t("new")} ${isEpic ? t("epic") : t("issue")}`}
); }); diff --git a/web/core/components/issues/issue-layouts/quick-add/button/list.tsx b/web/core/components/issues/issue-layouts/quick-add/button/list.tsx index 3dcbf5990a8..ff56eeb7508 100644 --- a/web/core/components/issues/issue-layouts/quick-add/button/list.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/button/list.tsx @@ -1,19 +1,20 @@ import { FC } from "react"; import { observer } from "mobx-react"; import { PlusIcon } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; import { Row } from "@plane/ui"; import { TQuickAddIssueButton } from "../root"; export const ListQuickAddIssueButton: FC = observer((props) => { const { onClick, isEpic = false } = props; - + const { t } = useTranslation(); return ( - {`New ${isEpic ? "Epic" : "Issue"}`} + {`${t("new")} ${isEpic ? t("epic") : t("issue")}`} ); }); diff --git a/web/core/components/issues/issue-layouts/quick-add/button/spreadsheet.tsx b/web/core/components/issues/issue-layouts/quick-add/button/spreadsheet.tsx index 170f891909c..3aba0657d88 100644 --- a/web/core/components/issues/issue-layouts/quick-add/button/spreadsheet.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/button/spreadsheet.tsx @@ -1,11 +1,12 @@ import { FC } from "react"; import { observer } from "mobx-react"; import { PlusIcon } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; import { TQuickAddIssueButton } from "../root"; export const SpreadsheetAddIssueButton: FC = observer((props) => { const { onClick, isEpic = false } = props; - + const { t } = useTranslation(); return (
); diff --git a/web/core/components/issues/issue-layouts/quick-add/form/calendar.tsx b/web/core/components/issues/issue-layouts/quick-add/form/calendar.tsx index 8b8d108d5e8..588ec7ce439 100644 --- a/web/core/components/issues/issue-layouts/quick-add/form/calendar.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/form/calendar.tsx @@ -1,10 +1,11 @@ import { FC } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { TQuickAddIssueForm } from "../root"; export const CalendarQuickAddIssueForm: FC = observer((props) => { const { ref, isOpen, projectDetail, register, onSubmit, isEpic } = props; - + const { t } = useTranslation(); return (
= observer((props diff --git a/web/core/components/issues/issue-layouts/quick-add/form/gantt.tsx b/web/core/components/issues/issue-layouts/quick-add/form/gantt.tsx index e9b7bb38d0c..c61e33ac1d8 100644 --- a/web/core/components/issues/issue-layouts/quick-add/form/gantt.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/form/gantt.tsx @@ -1,11 +1,12 @@ import { FC } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { cn } from "@/helpers/common.helper"; import { TQuickAddIssueForm } from "../root"; export const GanttQuickAddIssueForm: FC = observer((props) => { const { ref, projectDetail, hasError, register, onSubmit, isEpic } = props; - + const { t } = useTranslation(); return (
= observer((props) =
-
{`Press 'Enter' to add another ${isEpic ? "epic" : "issue"}`}
+
{`${t("press_enter_to_add_another")} ${isEpic ? t("epic") : t("issue")}`}
); }); diff --git a/web/core/components/issues/issue-layouts/quick-add/form/kanban.tsx b/web/core/components/issues/issue-layouts/quick-add/form/kanban.tsx index 1f25be6e942..a48d5c7f66d 100644 --- a/web/core/components/issues/issue-layouts/quick-add/form/kanban.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/form/kanban.tsx @@ -1,10 +1,11 @@ import { FC } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { TQuickAddIssueForm } from "../root"; export const KanbanQuickAddIssueForm: FC = observer((props) => { const { ref, projectDetail, register, onSubmit, isEpic } = props; - + const { t } = useTranslation(); return (
@@ -12,15 +13,15 @@ export const KanbanQuickAddIssueForm: FC = observer((props)

{projectDetail?.identifier ?? "..."}

-
{`Press 'Enter' to add another ${isEpic ? "epic" : "issue"}`}
+
{`${t("press_enter_to_add_another")} ${isEpic ? t("epic") : t("issue")}`}
); }); diff --git a/web/core/components/issues/issue-layouts/quick-add/form/list.tsx b/web/core/components/issues/issue-layouts/quick-add/form/list.tsx index 75e2c8d3a9a..6b12a2055df 100644 --- a/web/core/components/issues/issue-layouts/quick-add/form/list.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/form/list.tsx @@ -1,10 +1,11 @@ import { FC } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { TQuickAddIssueForm } from "../root"; export const ListQuickAddIssueForm: FC = observer((props) => { const { ref, projectDetail, register, onSubmit, isEpic } = props; - + const { t } = useTranslation(); return (
= observer((props) =>
-
{`Press 'Enter' to add another ${isEpic ? "epic" : "issue"}`}
+
{`${t("press_enter_to_add_another")} ${isEpic ? t("epic") : t("issue")}`}
); }); diff --git a/web/core/components/issues/issue-layouts/quick-add/form/spreadsheet.tsx b/web/core/components/issues/issue-layouts/quick-add/form/spreadsheet.tsx index 4918157c74f..2e0ef7b5ab3 100644 --- a/web/core/components/issues/issue-layouts/quick-add/form/spreadsheet.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/form/spreadsheet.tsx @@ -1,10 +1,11 @@ import { FC } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; import { TQuickAddIssueForm } from "../root"; export const SpreadsheetQuickAddIssueForm: FC = observer((props) => { const { ref, projectDetail, register, onSubmit, isEpic } = props; - + const { t } = useTranslation(); return (
= observer((pr

- {`Press Enter to add another ${isEpic ? "epic" : "issue"}`} + {`${t("press_enter_to_add_another")} ${isEpic ? t("epic") : t("issue")}`}

); diff --git a/web/core/components/issues/issue-layouts/quick-add/root.tsx b/web/core/components/issues/issue-layouts/quick-add/root.tsx index 73be7ef1fc3..869f28e6d97 100644 --- a/web/core/components/issues/issue-layouts/quick-add/root.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/root.tsx @@ -7,6 +7,7 @@ import { useForm, UseFormRegister } from "react-hook-form"; import { PlusIcon } from "lucide-react"; // plane constants import { EIssueLayoutTypes, EIssueServiceType } from "@plane/constants"; +import { useTranslation } from "@plane/i18n"; // types import { IProject, TIssue } from "@plane/types"; // ui @@ -66,6 +67,8 @@ export const QuickAddIssueRoot: FC = observer((props) => { quickAddCallback, isEpic = false, } = props; + // i18n + const { t } = useTranslation(); // router const { workspaceSlug, projectId } = useParams(); const pathname = usePathname(); @@ -115,8 +118,8 @@ export const QuickAddIssueRoot: FC = observer((props) => { setPromiseToast(quickAddPromise, { loading: `Adding ${isEpic ? "epic" : "issue"}...`, success: { - title: "Success!", - message: () => `${isEpic ? "Epic" : "Issue"} created successfully.`, + title: t("success"), + message: () => `${isEpic ? t("epic") : t("issue")} ${t("created_successfully")}.`, actionItems: (data) => ( = observer((props) => { ), }, error: { - title: "Error!", - message: (err) => err?.message || "Some error occurred. Please try again.", + title: t("error"), + message: (err) => err?.message || t("some_error_occurred_please_try_again"), }, }); @@ -160,18 +163,18 @@ export const QuickAddIssueRoot: FC = observer((props) => { )} > {isOpen ? ( - handleIsOpen(false)} - isEpic={isEpic} - /> + handleIsOpen(false)} + isEpic={isEpic} + /> ) : ( <> {QuickAddButton && handleIsOpen(true)} />} @@ -182,7 +185,7 @@ export const QuickAddIssueRoot: FC = observer((props) => { onClick={() => handleIsOpen(true)} > - {`New ${isEpic ? "Epic" : "Issue"}`} + {`${t("new")} ${isEpic ? t("epic") : t("issue")}`} )} diff --git a/web/core/components/issues/issue-layouts/save-filter-view.tsx b/web/core/components/issues/issue-layouts/save-filter-view.tsx index f4a0eca280b..579878197df 100644 --- a/web/core/components/issues/issue-layouts/save-filter-view.tsx +++ b/web/core/components/issues/issue-layouts/save-filter-view.tsx @@ -1,6 +1,7 @@ "use client"; import { FC, useState } from "react"; +import { useTranslation } from "@plane/i18n"; import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions } from "@plane/types"; import { Button } from "@plane/ui"; // components @@ -18,6 +19,7 @@ interface ISaveFilterView { export const SaveFilterView: FC = (props) => { const { workspaceSlug, projectId, filterParams } = props; + const { t } = useTranslation(); const [viewModal, setViewModal] = useState(false); @@ -32,7 +34,7 @@ export const SaveFilterView: FC = (props) => { /> ); diff --git a/web/core/components/issues/issue-modal/form.tsx b/web/core/components/issues/issue-modal/form.tsx index acc9c67e021..67513b7e286 100644 --- a/web/core/components/issues/issue-modal/form.tsx +++ b/web/core/components/issues/issue-modal/form.tsx @@ -539,7 +539,7 @@ export const IssueFormRoot: FC = observer((props) => { onClick={handleMoveToProjects} disabled={isMoving} > - Add to project + {t("add_to_project")} )} diff --git a/web/core/components/issues/parent-issues-list-modal.tsx b/web/core/components/issues/parent-issues-list-modal.tsx index 5d2d46caf68..b1f6bfea57a 100644 --- a/web/core/components/issues/parent-issues-list-modal.tsx +++ b/web/core/components/issues/parent-issues-list-modal.tsx @@ -6,6 +6,7 @@ import { useParams } from "next/navigation"; import { Rocket, Search } from "lucide-react"; // headless ui import { Combobox, Dialog, Transition } from "@headlessui/react"; +import { useTranslation } from "@plane/i18n"; // types import { ISearchIssueResponse } from "@plane/types"; // ui @@ -44,6 +45,7 @@ export const ParentIssuesListModal: React.FC = ({ issueId, searchEpic = false, }) => { + const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(false); const [searchTerm, setSearchTerm] = useState(""); const [issues, setIssues] = useState([]); @@ -122,7 +124,7 @@ export const ParentIssuesListModal: React.FC = ({ /> setSearchTerm(e.target.value)} displayValue={() => ""} @@ -135,13 +137,13 @@ export const ParentIssuesListModal: React.FC = ({ > {searchTerm !== "" && (
- Search results for{" "} + {t("search_results_for")} {'"'} {searchTerm} {'"'} {" "} - in project: + {t("in_project")}:
)} diff --git a/web/core/components/issues/peek-overview/root.tsx b/web/core/components/issues/peek-overview/root.tsx index e010d6717e0..94d2fd1a03c 100644 --- a/web/core/components/issues/peek-overview/root.tsx +++ b/web/core/components/issues/peek-overview/root.tsx @@ -5,6 +5,7 @@ import { observer } from "mobx-react"; import { usePathname } from "next/navigation"; // plane types import { EIssuesStoreType } from "@plane/constants"; +import { useTranslation } from "@plane/i18n"; import { TIssue } from "@plane/types"; // plane ui import { TOAST_TYPE, setPromiseToast, setToast } from "@plane/ui"; @@ -37,6 +38,8 @@ export const IssuePeekOverview: FC = observer((props) => { // store hook const { allowPermissions } = useUserPermissions(); + const { t } = useTranslation(); + const { issues: { restoreIssue }, } = useIssues(EIssuesStoreType.ARCHIVED); @@ -111,9 +114,9 @@ export const IssuePeekOverview: FC = observer((props) => { }); } catch { setToast({ - title: "Error!", + title: t("error"), type: TOAST_TYPE.ERROR, - message: "Issue delete failed", + message: t("issue_delete_failed"), }); captureIssueEvent({ eventName: ISSUE_DELETED, @@ -144,8 +147,8 @@ export const IssuePeekOverview: FC = observer((props) => { await restoreIssue(workspaceSlug, projectId, issueId); setToast({ type: TOAST_TYPE.SUCCESS, - title: "Restore success", - message: "Your issue can be found in project issues.", + title: t("restore_success"), + message: t("your_issue_can_be_found_in_project_issues"), }); captureIssueEvent({ eventName: ISSUE_RESTORED, @@ -155,8 +158,8 @@ export const IssuePeekOverview: FC = observer((props) => { } catch { setToast({ type: TOAST_TYPE.ERROR, - title: "Error!", - message: "Issue could not be restored. Please try again.", + title: t("error"), + message: t("issue_could_not_be_restored_please_try_again"), }); captureIssueEvent({ eventName: ISSUE_RESTORED, diff --git a/web/core/components/project/applied-filters/date.tsx b/web/core/components/project/applied-filters/date.tsx index 577015ad58e..df28b20c560 100644 --- a/web/core/components/project/applied-filters/date.tsx +++ b/web/core/components/project/applied-filters/date.tsx @@ -1,5 +1,6 @@ import { observer } from "mobx-react"; import { X } from "lucide-react"; +import { useTranslation } from "@plane/i18n"; // helpers import { PROJECT_CREATED_AT_FILTER_OPTIONS } from "@/constants/filters"; import { renderFormattedDate } from "@/helpers/date-time.helper"; @@ -14,13 +15,15 @@ type Props = { export const AppliedDateFilters: React.FC = observer((props) => { const { editable, handleRemove, values } = props; + // i18n + const { t } = useTranslation(); const getDateLabel = (value: string): string => { let dateLabel = ""; const dateDetails = PROJECT_CREATED_AT_FILTER_OPTIONS.find((d) => d.value === value); - if (dateDetails) dateLabel = dateDetails.name; + if (dateDetails) dateLabel = t(dateDetails.nameTranslationKey); else { const dateParts = value.split(";"); diff --git a/web/core/components/project/dropdowns/filters/created-at.tsx b/web/core/components/project/dropdowns/filters/created-at.tsx index 85a2c3b9598..2ece2fd2716 100644 --- a/web/core/components/project/dropdowns/filters/created-at.tsx +++ b/web/core/components/project/dropdowns/filters/created-at.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; import { observer } from "mobx-react"; +import { useTranslation } from "@plane/i18n"; // components import { DateFilterModal } from "@/components/core"; import { FilterHeader, FilterOption } from "@/components/issues"; @@ -16,6 +17,8 @@ type Props = { export const FilterCreatedDate: React.FC = observer((props) => { const { appliedFilters, handleUpdate, searchQuery } = props; + // i18n + const { t } = useTranslation(); // state const [previewEnabled, setPreviewEnabled] = useState(true); const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false); @@ -60,19 +63,19 @@ export const FilterCreatedDate: React.FC = observer((props) => { key={option.value} isChecked={appliedFilters?.includes(option.value) ? true : false} onClick={() => handleUpdate(option.value)} - title={option.name} + title={t(option.nameTranslationKey)} multiple={false} /> ))} ) : ( -

No matches found

+

{t("no_matches_found")}

)} )} diff --git a/web/core/constants/filters.ts b/web/core/constants/filters.ts index 98189bc78eb..b6a0af80c82 100644 --- a/web/core/constants/filters.ts +++ b/web/core/constants/filters.ts @@ -1,18 +1,22 @@ export const DATE_AFTER_FILTER_OPTIONS = [ { name: "1 week from now", + nameTranslationKey: "1_week_from_now", value: "1_weeks;after;fromnow", }, { name: "2 weeks from now", + nameTranslationKey: "2_weeks_from_now", value: "2_weeks;after;fromnow", }, { name: "1 month from now", + nameTranslationKey: "1_month_from_now", value: "1_months;after;fromnow", }, { name: "2 months from now", + nameTranslationKey: "2_months_from_now", value: "2_months;after;fromnow", }, ]; @@ -20,14 +24,17 @@ export const DATE_AFTER_FILTER_OPTIONS = [ export const DATE_BEFORE_FILTER_OPTIONS = [ { name: "1 week ago", + nameTranslationKey: "1_week_ago", value: "1_weeks;before;fromnow", }, { name: "2 weeks ago", + nameTranslationKey: "2_weeks_ago", value: "2_weeks;before;fromnow", }, { name: "1 month ago", + nameTranslationKey: "1_month_ago", value: "1_months;before;fromnow", }, ]; @@ -36,17 +43,21 @@ export const PROJECT_CREATED_AT_FILTER_OPTIONS = [ { name: "Today", value: "today;custom;custom", + nameTranslationKey: "today", }, { name: "Yesterday", value: "yesterday;custom;custom", + nameTranslationKey: "yesterday", }, { name: "Last 7 days", value: "last_7_days;custom;custom", + nameTranslationKey: "last_7_days", }, { name: "Last 30 days", value: "last_30_days;custom;custom", + nameTranslationKey: "last_30_days", }, ]; diff --git a/web/core/constants/issue.ts b/web/core/constants/issue.ts index 02d7ee01ad3..2970bf7cb43 100644 --- a/web/core/constants/issue.ts +++ b/web/core/constants/issue.ts @@ -36,50 +36,54 @@ export type TCreateModalStoreTypes = export const ISSUE_PRIORITIES: { key: TIssuePriorities; title: string; + titleTranslationKey: string; }[] = [ - { key: "urgent", title: "Urgent" }, - { key: "high", title: "High" }, - { key: "medium", title: "Medium" }, - { key: "low", title: "Low" }, - { key: "none", title: "None" }, + { key: "urgent", title: "Urgent", titleTranslationKey: "urgent" }, + { key: "high", title: "High", titleTranslationKey: "high" }, + { key: "medium", title: "Medium", titleTranslationKey: "medium" }, + { key: "low", title: "Low", titleTranslationKey: "low" }, + { key: "none", title: "None", titleTranslationKey: "none" }, ]; export const ISSUE_GROUP_BY_OPTIONS: { key: TIssueGroupByOptions; title: string; + titleTranslationKey: string; }[] = [ - { key: "state", title: "States" }, - { key: "state_detail.group", title: "State Groups" }, - { key: "priority", title: "Priority" }, - { key: "team_project", title: "Team Project" }, // required this on team issues - { key: "project", title: "Project" }, // required this on my issues - { key: "cycle", title: "Cycle" }, // required this on my issues - { key: "module", title: "Module" }, // required this on my issues - { key: "labels", title: "Labels" }, - { key: "assignees", title: "Assignees" }, - { key: "created_by", title: "Created By" }, - { key: null, title: "None" }, + { key: "state", title: "States", titleTranslationKey: "states" }, + { key: "state_detail.group", title: "State Groups", titleTranslationKey: "state_groups" }, + { key: "priority", title: "Priority", titleTranslationKey: "priority" }, + { key: "team_project", title: "Team Project", titleTranslationKey: "team_project" }, // required this on team issues + { key: "project", title: "Project", titleTranslationKey: "project" }, // required this on my issues + { key: "cycle", title: "Cycle", titleTranslationKey: "cycle" }, // required this on my issues + { key: "module", title: "Module", titleTranslationKey: "module" }, // required this on my issues + { key: "labels", title: "Labels", titleTranslationKey: "labels" }, + { key: "assignees", title: "Assignees", titleTranslationKey: "assignees" }, + { key: "created_by", title: "Created By", titleTranslationKey: "created_by" }, + { key: null, title: "None", titleTranslationKey: "none" }, ]; export const ISSUE_ORDER_BY_OPTIONS: { key: TIssueOrderByOptions; title: string; + titleTranslationKey: string; }[] = [ - { key: "sort_order", title: "Manual" }, - { key: "-created_at", title: "Last Created" }, - { key: "-updated_at", title: "Last Updated" }, - { key: "start_date", title: "Start Date" }, - { key: "target_date", title: "Due Date" }, - { key: "-priority", title: "Priority" }, + { key: "sort_order", title: "Manual", titleTranslationKey: "manual" }, + { key: "-created_at", title: "Last Created", titleTranslationKey: "last_created" }, + { key: "-updated_at", title: "Last Updated", titleTranslationKey: "last_updated" }, + { key: "start_date", title: "Start Date", titleTranslationKey: "start_date" }, + { key: "target_date", title: "Due Date", titleTranslationKey: "due_date" }, + { key: "-priority", title: "Priority", titleTranslationKey: "priority" }, ]; export const ISSUE_FILTER_OPTIONS: { key: TIssueGroupingFilters; title: string; + titleTranslationKey: string; }[] = [ - { key: null, title: "All" }, - { key: "active", title: "Active" }, - { key: "backlog", title: "Backlog" }, + { key: null, title: "All", titleTranslationKey: "all" }, + { key: "active", title: "Active", titleTranslationKey: "active" }, + { key: "backlog", title: "Backlog", titleTranslationKey: "backlog" }, // { key: "draft", title: "Draft Issues" }, ]; @@ -109,50 +113,72 @@ export const EPICS_DISPLAY_PROPERTIES_KEYS: (keyof IIssueDisplayProperties)[] = export const ISSUE_DISPLAY_PROPERTIES: { key: keyof IIssueDisplayProperties; title: string; + titleTranslationKey: string; }[] = [ - { key: "key", title: "ID" }, - { key: "issue_type", title: "Issue Type" }, - { key: "assignee", title: "Assignee" }, - { key: "start_date", title: "Start date" }, - { key: "due_date", title: "Due date" }, - { key: "labels", title: "Labels" }, - { key: "priority", title: "Priority" }, - { key: "state", title: "State" }, - { key: "sub_issue_count", title: "Sub issue count" }, - { key: "attachment_count", title: "Attachment count" }, - { key: "link", title: "Link" }, - { key: "estimate", title: "Estimate" }, - { key: "modules", title: "Modules" }, - { key: "cycle", title: "Cycle" }, + { key: "key", title: "ID", titleTranslationKey: "id" }, + { key: "issue_type", title: "Issue Type", titleTranslationKey: "issue_type" }, + { key: "assignee", title: "Assignee", titleTranslationKey: "assignee" }, + { key: "start_date", title: "Start date", titleTranslationKey: "start_date" }, + { key: "due_date", title: "Due date", titleTranslationKey: "due_date" }, + { key: "labels", title: "Labels", titleTranslationKey: "labels" }, + { key: "priority", title: "Priority", titleTranslationKey: "priority" }, + { key: "state", title: "State", titleTranslationKey: "state" }, + { key: "sub_issue_count", title: "Sub issue count", titleTranslationKey: "sub_issue_count" }, + { key: "attachment_count", title: "Attachment count", titleTranslationKey: "attachment_count" }, + { key: "link", title: "Link", titleTranslationKey: "link" }, + { key: "estimate", title: "Estimate", titleTranslationKey: "estimate" }, + { key: "modules", title: "Modules", titleTranslationKey: "modules" }, + { key: "cycle", title: "Cycle", titleTranslationKey: "cycle" }, ]; export const ISSUE_EXTRA_OPTIONS: { key: TIssueExtraOptions; title: string; + titleTranslationKey: string; }[] = [ - { key: "sub_issue", title: "Show sub-issues" }, // in spreadsheet its always false - { key: "show_empty_groups", title: "Show empty groups" }, // filter on front-end + { key: "sub_issue", title: "Show sub-issues", titleTranslationKey: "show_sub_issues" }, // in spreadsheet its always false + { key: "show_empty_groups", title: "Show empty groups", titleTranslationKey: "show_empty_groups" }, // filter on front-end ]; export const ISSUE_LAYOUT_MAP = { - [EIssueLayoutTypes.LIST]: { key: EIssueLayoutTypes.LIST, title: "List layout", label: "List", icon: List }, - [EIssueLayoutTypes.KANBAN]: { key: EIssueLayoutTypes.KANBAN, title: "Board layout", label: "Board", icon: Kanban }, + [EIssueLayoutTypes.LIST]: { + key: EIssueLayoutTypes.LIST, + title: "List layout", + titleTranslationKey: "list_layout", + label: "List", + labelTranslationKey: "list", + icon: List, + }, + [EIssueLayoutTypes.KANBAN]: { + key: EIssueLayoutTypes.KANBAN, + title: "Board layout", + titleTranslationKey: "board_layout", + label: "Board", + labelTranslationKey: "board", + icon: Kanban, + }, [EIssueLayoutTypes.CALENDAR]: { key: EIssueLayoutTypes.CALENDAR, title: "Calendar layout", + titleTranslationKey: "calendar_layout", label: "Calendar", + labelTranslationKey: "calendar", icon: Calendar, }, [EIssueLayoutTypes.SPREADSHEET]: { key: EIssueLayoutTypes.SPREADSHEET, title: "Table layout", + titleTranslationKey: "table_layout", label: "Table", + labelTranslationKey: "table", icon: Sheet, }, [EIssueLayoutTypes.GANTT]: { key: EIssueLayoutTypes.GANTT, title: "Timeline layout", + titleTranslationKey: "timeline_layout", label: "Timeline", + labelTranslationKey: "timeline", icon: GanttChartSquare, }, }; @@ -160,6 +186,7 @@ export const ISSUE_LAYOUT_MAP = { export const ISSUE_LAYOUTS: { key: EIssueLayoutTypes; title: string; + titleTranslationKey: string; icon: any; }[] = Object.values(ISSUE_LAYOUT_MAP); From 937919ee1140af98ff55a880beef0f77deca6af2 Mon Sep 17 00:00:00 2001 From: Vamsi krishna Date: Fri, 17 Jan 2025 17:42:45 +0530 Subject: [PATCH 2/2] * chore: refactored translations * imoprovement: added chinese translations --- .../i18n/src/locales/en/translations.json | 19 +++- .../i18n/src/locales/es/translations.json | 16 +++- .../i18n/src/locales/fr/translations.json | 16 +++- .../i18n/src/locales/ja/translations.json | 16 +++- .../i18n/src/locales/zh-CN/translations.json | 92 ++++++++++++++++++- web/ce/components/issues/header.tsx | 3 +- .../core/filters/date-filter-select.tsx | 42 +++++---- .../create-issue-toast-action-items.tsx | 8 +- .../issue-layouts/quick-add/form/gantt.tsx | 9 +- .../issue-layouts/quick-add/form/kanban.tsx | 6 +- .../issue-layouts/quick-add/form/list.tsx | 4 +- .../quick-add/form/spreadsheet.tsx | 2 +- .../issues/issue-layouts/quick-add/root.tsx | 28 +++--- .../components/issues/peek-overview/root.tsx | 32 +++---- 14 files changed, 218 insertions(+), 75 deletions(-) diff --git a/packages/i18n/src/locales/en/translations.json b/packages/i18n/src/locales/en/translations.json index f0fcc1a785b..ff0d257bb44 100644 --- a/packages/i18n/src/locales/en/translations.json +++ b/packages/i18n/src/locales/en/translations.json @@ -374,7 +374,6 @@ "show_empty_groups": "Show empty groups", "clear_all": "Clear all", "save_view": "Save view", - "press_enter_to_add_another": "Press 'Enter' to add another", "epic_title": "Epic title", "issue_title": "Issue title", "new": "New", @@ -393,5 +392,21 @@ "comment_update_failed_please_try_again_later": "Comment update failed. Please try again later.", "comment_removed_successfully": "Comment removed successfully", "comment_remove_failed_please_try_again_later": "Comment remove failed. Please try again later.", - "asset_upload_failed_please_try_again_later": "Asset upload failed. Please try again later." + "asset_upload_failed_please_try_again_later": "Asset upload failed. Please try again later.", + "issue_could_not_be_added_to_the_cycle_please_try_again": "Issue could not be added to the cycle. Please try again.", + "issue_removed_from_the_cycle_successfully": "Issue removed from the cycle successfully", + "issue_could_not_be_removed_from_the_cycle_please_try_again": "Issue could not be removed from the cycle. Please try again.", + "issue_removed_from_the_module_successfully": "Issue removed from the module successfully", + "issue_could_not_be_removed_from_the_module_please_try_again": "Issue could not be removed from the module. Please try again.", + "issue_update_failed_please_try_again_later": "Issue update failed. Please try again later.", + "project_issues_count": { + "one": "There is {{count}} issue in this project", + "other": "There are {{count}} issues in this project" + }, + "adding": "Adding {entity}...", + "entity_created_successfully": "{entity} created successfully", + "entity_title_required": "{entity} title is required.", + "some_error_occurred_please_try_again": "Some error occurred. Please try again.", + "press_enter_to_add_another_entity": "Press 'Enter' to add another {entity}", + "select_date": "Select date" } diff --git a/packages/i18n/src/locales/es/translations.json b/packages/i18n/src/locales/es/translations.json index 74d4999af30..d7a2aed80b6 100644 --- a/packages/i18n/src/locales/es/translations.json +++ b/packages/i18n/src/locales/es/translations.json @@ -374,7 +374,6 @@ "show_empty_groups": "Mostrar grupos vacíos", "clear_all": "Limpiar todo", "save_view": "Guardar vista", - "press_enter_to_add_another": "Presiona 'Enter' para agregar otro", "epic_title": "Título de epic", "issue_title": "Título de problema", "new": "Nuevo", @@ -393,5 +392,18 @@ "comment_update_failed_please_try_again_later": "Error al actualizar el comentario. Por favor, inténtalo de nuevo más tarde.", "comment_removed_successfully": "Comentario eliminado con éxito", "comment_remove_failed_please_try_again_later": "Error al eliminar el comentario. Por favor, inténtalo de nuevo más tarde.", - "asset_upload_failed_please_try_again_later": "Error al subir el archivo. Por favor, inténtalo de nuevo más tarde." + "asset_upload_failed_please_try_again_later": "Error al subir el archivo. Por favor, inténtalo de nuevo más tarde.", + "issue_could_not_be_added_to_the_cycle_please_try_again": "El problema no pudo ser agregado al ciclo. Por favor, inténtalo de nuevo.", + "issue_removed_from_the_cycle_successfully": "El problema fue removido del ciclo con éxito", + "issue_could_not_be_removed_from_the_cycle_please_try_again": "El problema no pudo ser removido del ciclo. Por favor, inténtalo de nuevo.", + "issue_removed_from_the_module_successfully": "El problema fue removido del módulo con éxito", + "issue_could_not_be_removed_from_the_module_please_try_again": "El problema no pudo ser removido del módulo. Por favor, inténtalo de nuevo.", + "issue_update_failed_please_try_again_later": "El problema no pudo ser actualizado. Por favor, inténtalo de nuevo más tarde.", + "project_issues_count": "{{count, plural, =0{Sin problemas} one{# problema} other{# problemas}}}", + "adding": "Agregando {entity}...", + "entity_created_successfully": "{entity} creado con éxito", + "entity_title_required": "El título de {entity} es obligatorio.", + "press_enter_to_add_another_entity": "Presiona 'Enter' para agregar otro {entity}", + "some_error_occurred_please_try_again": "Ocurrió un error. Por favor, inténtalo de nuevo.", + "select_date": "Seleccionar fecha" } diff --git a/packages/i18n/src/locales/fr/translations.json b/packages/i18n/src/locales/fr/translations.json index 3f67a1d7c84..352c75d747d 100644 --- a/packages/i18n/src/locales/fr/translations.json +++ b/packages/i18n/src/locales/fr/translations.json @@ -374,7 +374,6 @@ "show_empty_groups": "Afficher les groupes vides", "clear_all": "Tout effacer", "save_view": "Enregistrer la vue", - "press_enter_to_add_another": "'Entrer' pour ajouter un autre", "epic_title": "Titre de l'épique", "issue_title": "Titre du problème", "new": "Nouveau", @@ -393,5 +392,18 @@ "comment_update_failed_please_try_again_later": "Erreur lors de la mise à jour du commentaire. Veuillez réessayer plus tard.", "comment_removed_successfully": "Commentaire supprimé avec succès", "comment_remove_failed_please_try_again_later": "Erreur lors de la suppression du commentaire. Veuillez réessayer plus tard.", - "asset_upload_failed_please_try_again_later": "Erreur lors de l'upload de l'asset. Veuillez réessayer plus tard." + "asset_upload_failed_please_try_again_later": "Erreur lors de l'upload de l'asset. Veuillez réessayer plus tard.", + "issue_could_not_be_added_to_the_cycle_please_try_again": "Le problème n'a pas pu être ajouté au cycle. Veuillez réessayer.", + "issue_removed_from_the_cycle_successfully": "Le problème a été supprimé du cycle avec succès", + "issue_could_not_be_removed_from_the_cycle_please_try_again": "Le problème n'a pas pu être supprimé du cycle. Veuillez réessayer.", + "issue_removed_from_the_module_successfully": "Le problème a été supprimé du module avec succès", + "issue_could_not_be_removed_from_the_module_please_try_again": "Le problème n'a pas pu être supprimé du module. Veuillez réessayer.", + "issue_update_failed_please_try_again_later": "Le problème n'a pas pu être mis à jour. Veuillez réessayer plus tard.", + "project_issues_count": "{{count, plural, =0{Aucun problème} one{# problème} other{# problèmes}}}", + "adding_issue": "Ajout de {entity}...", + "entity_created_successfully": "{entity} créé avec succès", + "entity_title_required": "{entity} title is required.", + "press_enter_to_add_another_entity": "Appuyez sur 'Entrer' pour ajouter un autre {entity}", + "some_error_occurred_please_try_again": "Une erreur est survenue. Veuillez réessayer.", + "select_date": "Sélectionner la date" } diff --git a/packages/i18n/src/locales/ja/translations.json b/packages/i18n/src/locales/ja/translations.json index d62f3555b6d..5e6f60238c5 100644 --- a/packages/i18n/src/locales/ja/translations.json +++ b/packages/i18n/src/locales/ja/translations.json @@ -374,7 +374,6 @@ "show_empty_groups": "空のグループを表示", "clear_all": "すべてクリア", "save_view": "ビューを保存", - "press_enter_to_add_another": "'Enter'を押して別の", "epic_title": "エピックタイトル", "issue_title": "問題タイトル", "new": "新しい", @@ -393,5 +392,18 @@ "comment_update_failed_please_try_again_later": "コメントの更新に失敗しました。もう一度お試しください。", "comment_removed_successfully": "コメントが正常に削除されました", "comment_remove_failed_please_try_again_later": "コメントの削除に失敗しました。もう一度お試しください。", - "asset_upload_failed_please_try_again_later": "アセットのアップロードに失敗しました。もう一度お試しください。" + "asset_upload_failed_please_try_again_later": "アセットのアップロードに失敗しました。もう一度お試しください。", + "issue_could_not_be_added_to_the_cycle_please_try_again": "問題をサイクルに追加できませんでした。もう一度お試しください。", + "issue_removed_from_the_cycle_successfully": "問題がサイクルから正常に削除されました", + "issue_could_not_be_removed_from_the_cycle_please_try_again": "問題をサイクルから削除できませんでした。もう一度お試しください。", + "issue_removed_from_the_module_successfully": "問題がモジュールから正常に削除されました", + "issue_could_not_be_removed_from_the_module_please_try_again": "問題をモジュールから削除できませんでした。もう一度お試しください。", + "issue_update_failed_please_try_again_later": "問題を更新できませんでした。もう一度お試しください。", + "project_issues_count": "{{count, plural, =0{問題なし} one{# 問題} other{# 問題}}", + "adding_issue": "{entity}を追加中...", + "entity_created_successfully": "{entity}が正常に作成されました", + "entity_title_required": "{entity}タイトルは必須です。", + "press_enter_to_add_another_entity": "'Enter'を押して別の{entity}を追加", + "some_error_occurred_please_try_again": "エラーが発生しました。もう一度お試しください。", + "select_date": "日付を選択" } diff --git a/packages/i18n/src/locales/zh-CN/translations.json b/packages/i18n/src/locales/zh-CN/translations.json index 093027c16b1..0418158a8a1 100644 --- a/packages/i18n/src/locales/zh-CN/translations.json +++ b/packages/i18n/src/locales/zh-CN/translations.json @@ -282,6 +282,7 @@ "create_a_draft": "创建草稿", "save_to_drafts": "保存为草稿", "save": "保存", + "update": "更新", "updating": "正在更新", "create_new_issue": "创建新问题", "editor_is_not_ready_to_discard_changes": "编辑器尚未准备好丢弃更改", @@ -315,5 +316,94 @@ "remove_parent_issue": "移除父问题", "add_parent": "添加父问题", "loading_members": "正在加载成员...", - "inbox": "收件箱" + "inbox": "收件箱", + "issue": "问题", + "layout": "布局", + "filters": "筛选", + "display": "显示", + "display_properties": "显示属性", + "list_layout": "列表布局", + "board_layout": "看板布局", + "calendar_layout": "日历布局", + "table_layout": "表格布局", + "timeline_layout": "时间线布局", + "no_cycle": "无周期", + "no_matches_found": "未找到匹配项", + "module": "模块", + "no_module": "无模块", + "state_group": "状态组", + "view_less": "显示更少", + "view_all": "显示全部", + "mention": "提及", + "created_by": "创建者", + "label": "标签", + "epic": "史诗", + "epics": "史诗", + "grouping": "分组", + "apply": "应用", + "after": "之后", + "before": "之前", + "range": "范围", + "all": "全部", + "active": "活跃", + "backlog": "待办", + "target_date": "截止日期", + "search_results_for": "搜索结果", + "in_project": "在项目中", + "type_to_search": "输入以搜索...", + "1_week_ago": "1周前", + "2_weeks_ago": "2周前", + "1_month_ago": "1个月前", + "2_months_ago": "2个月前", + "1_week_from_now": "1周后", + "2_weeks_from_now": "2周后", + "1_month_from_now": "1个月后", + "2_months_from_now": "2个月后", + "id": "ID", + "sub_issue_count": "子问题数量", + "attachment_count": "附件数量", + "link": "链接", + "group_by": "分组依据", + "sub_group_by": "子分组依据", + "states": "状态", + "manual": "手动", + "last_created": "最近创建", + "last_updated": "最近更新", + "order_by": "排序依据", + "show_sub_issues": "显示子问题", + "show_empty_groups": "显示空分组", + "clear_all": "清除全部", + "save_view": "保存视图", + "epic_title": "史诗标题", + "issue_title": "问题标题", + "new": "新建", + "created_successfully": "创建成功", + "view": "视图", + "copied": "已复制", + "your_issue_can_be_found_in_project_issues": "您的问题可以在项目问题中找到。", + "restore_success": "恢复成功", + "issue_could_not_be_restored_please_try_again": "无法恢复问题,请重试。", + "issue_delete_failed": "问题删除失败", + "updates": "更新", + "comment_created_successfully": "评论创建成功", + "comment_creation_failed_please_try_again_later": "评论创建失败,请稍后重试。", + "missing_fields": "缺少字段", + "comment_updated_successfully": "评论更新成功", + "comment_update_failed_please_try_again_later": "评论更新失败,请稍后重试。", + "comment_removed_successfully": "评论删除成功", + "comment_remove_failed_please_try_again_later": "评论删除失败,请稍后重试。", + "asset_upload_failed_please_try_again_later": "资源上传失败,请稍后重试。", + "issue_could_not_be_added_to_the_cycle_please_try_again": "无法将问题添加到周期,请重试。", + "issue_removed_from_the_cycle_successfully": "问题已成功从周期中移除", + "issue_could_not_be_removed_from_the_cycle_please_try_again": "无法从周期中移除问题,请重试。", + "issue_removed_from_the_module_successfully": "问题已成功从模块中移除", + "issue_could_not_be_removed_from_the_module_please_try_again": "无法从模块中移除问题,请重试。", + "issue_update_failed_please_try_again_later": "问题更新失败,请稍后重试。", + "project_issues_count": "{count, plural, =0{无问题} one{# 个问题} other{# 个问题}}", + "adding": "正在添加{entity}...", + "entity_created_successfully": "{entity}创建成功", + "entity_title_required": "{entity}标题为必填项。", + "press_enter_to_add_another_entity": "按回车键添加另一个{entity}", + "some_error_occurred_please_try_again": "发生错误,请重试。", + "select_date": "选择日期" } diff --git a/web/ce/components/issues/header.tsx b/web/ce/components/issues/header.tsx index c8d6d2b897d..711dfa65504 100644 --- a/web/ce/components/issues/header.tsx +++ b/web/ce/components/issues/header.tsx @@ -67,8 +67,7 @@ export const IssuesHeader = observer(() => { {issuesCount && issuesCount > 0 ? ( 1 ? "issues" : "issue"} in this project`} + tooltipContent={t("project_issues_count", { count: issuesCount })} position="bottom" > diff --git a/web/core/components/core/filters/date-filter-select.tsx b/web/core/components/core/filters/date-filter-select.tsx index 064c215c1e8..b61e30ed32e 100644 --- a/web/core/components/core/filters/date-filter-select.tsx +++ b/web/core/components/core/filters/date-filter-select.tsx @@ -41,25 +41,27 @@ const dueDateRange: DueDate[] = [ export const DateFilterSelect: React.FC = ({ title, value, onChange }) => { const { t } = useTranslation(); - return - {dueDateRange.find((item) => item.value === value)?.icon} - - {title} {t(dueDateRange.find((item) => item.value === value)?.nameTranslationKey || "")} - - - } - onChange={onChange} - > - {dueDateRange.map((option, index) => ( - -
- {option.icon} - {title} {t(option.nameTranslationKey)} + return ( + + {dueDateRange.find((item) => item.value === value)?.icon} + + {title} {t(dueDateRange.find((item) => item.value === value)?.nameTranslationKey || "select_date")} +
-
- ))} -
+ } + onChange={onChange} + > + {dueDateRange.map((option, index) => ( + +
+ {option.icon} + {title} {t(option.nameTranslationKey)} +
+
+ ))} +
+ ); }; diff --git a/web/core/components/issues/create-issue-toast-action-items.tsx b/web/core/components/issues/create-issue-toast-action-items.tsx index ad8635d6c56..2e83d83d7e3 100644 --- a/web/core/components/issues/create-issue-toast-action-items.tsx +++ b/web/core/components/issues/create-issue-toast-action-items.tsx @@ -2,7 +2,6 @@ import React, { FC, useState } from "react"; import { observer } from "mobx-react"; // helpers -import { useTranslation } from "@plane/i18n"; import { copyUrlToClipboard } from "@/helpers/string.helper"; // hooks import { useIssueDetail } from "@/hooks/store"; @@ -16,7 +15,6 @@ type TCreateIssueToastActionItems = { export const CreateIssueToastActionItems: FC = observer((props) => { const { workspaceSlug, projectId, issueId, isEpic = false } = props; - const { t } = useTranslation(); // state const [copied, setCopied] = useState(false); // store hooks @@ -51,12 +49,12 @@ export const CreateIssueToastActionItems: FC = obs rel="noopener noreferrer" className="text-custom-primary px-2 py-1 hover:bg-custom-background-90 font-medium rounded" > - {`${t("view")} ${isEpic ? t("epic") : t("issue")}`} + {`View ${isEpic ? "Epic" : "Issue"}`} {copied ? ( <> - {t("copied")} + Copied ) : ( <> @@ -64,7 +62,7 @@ export const CreateIssueToastActionItems: FC = obs className="cursor-pointer hidden group-hover:flex px-2 py-1 text-custom-text-300 hover:text-custom-text-200 hover:bg-custom-background-90 rounded" onClick={copyToClipboard} > - {t("copy_link")} + Copy Link )} diff --git a/web/core/components/issues/issue-layouts/quick-add/form/gantt.tsx b/web/core/components/issues/issue-layouts/quick-add/form/gantt.tsx index c61e33ac1d8..e9b7bb38d0c 100644 --- a/web/core/components/issues/issue-layouts/quick-add/form/gantt.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/form/gantt.tsx @@ -1,12 +1,11 @@ import { FC } from "react"; import { observer } from "mobx-react"; -import { useTranslation } from "@plane/i18n"; import { cn } from "@/helpers/common.helper"; import { TQuickAddIssueForm } from "../root"; export const GanttQuickAddIssueForm: FC = observer((props) => { const { ref, projectDetail, hasError, register, onSubmit, isEpic } = props; - const { t } = useTranslation(); + return (
= observer((props) =
-
{`${t("press_enter_to_add_another")} ${isEpic ? t("epic") : t("issue")}`}
+
{`Press 'Enter' to add another ${isEpic ? "epic" : "issue"}`}
); }); diff --git a/web/core/components/issues/issue-layouts/quick-add/form/kanban.tsx b/web/core/components/issues/issue-layouts/quick-add/form/kanban.tsx index a48d5c7f66d..df434623c4d 100644 --- a/web/core/components/issues/issue-layouts/quick-add/form/kanban.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/form/kanban.tsx @@ -15,13 +15,15 @@ export const KanbanQuickAddIssueForm: FC = observer((props) autoComplete="off" placeholder={isEpic ? t("epic_title") : t("issue_title")} {...register("name", { - required: `${isEpic ? t("epic") : t("issue")} ${t("title_is_required")}.`, + required: t("entity_title_required", { entity: isEpic ? t("epic") : t("issue") }), })} className="w-full rounded-md bg-transparent px-2 py-1.5 pl-0 text-sm font-medium leading-5 text-custom-text-200 outline-none" /> -
{`${t("press_enter_to_add_another")} ${isEpic ? t("epic") : t("issue")}`}
+
+ {t("press_enter_to_add_another_entity", { entity: isEpic ? t("epic") : t("issue") })} +
); }); diff --git a/web/core/components/issues/issue-layouts/quick-add/form/list.tsx b/web/core/components/issues/issue-layouts/quick-add/form/list.tsx index 6b12a2055df..829e97752f5 100644 --- a/web/core/components/issues/issue-layouts/quick-add/form/list.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/form/list.tsx @@ -26,7 +26,9 @@ export const ListQuickAddIssueForm: FC = observer((props) => /> -
{`${t("press_enter_to_add_another")} ${isEpic ? t("epic") : t("issue")}`}
+
+ {t("press_enter_to_add_another_entity", { entity: isEpic ? t("epic") : t("issue") })} +
); }); diff --git a/web/core/components/issues/issue-layouts/quick-add/form/spreadsheet.tsx b/web/core/components/issues/issue-layouts/quick-add/form/spreadsheet.tsx index 2e0ef7b5ab3..52f981d22de 100644 --- a/web/core/components/issues/issue-layouts/quick-add/form/spreadsheet.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/form/spreadsheet.tsx @@ -25,7 +25,7 @@ export const SpreadsheetQuickAddIssueForm: FC = observer((pr />

- {`${t("press_enter_to_add_another")} ${isEpic ? t("epic") : t("issue")}`} + {t("press_enter_to_add_another_entity", { entity: isEpic ? t("epic") : t("issue") })}

); diff --git a/web/core/components/issues/issue-layouts/quick-add/root.tsx b/web/core/components/issues/issue-layouts/quick-add/root.tsx index 869f28e6d97..0db0c47e3ea 100644 --- a/web/core/components/issues/issue-layouts/quick-add/root.tsx +++ b/web/core/components/issues/issue-layouts/quick-add/root.tsx @@ -116,10 +116,10 @@ export const QuickAddIssueRoot: FC = observer((props) => { if (quickAddCallback) { const quickAddPromise = quickAddCallback(projectId.toString(), { ...payload }); setPromiseToast(quickAddPromise, { - loading: `Adding ${isEpic ? "epic" : "issue"}...`, + loading: t("adding_issue", { entity: isEpic ? t("epic") : t("issue") }), success: { title: t("success"), - message: () => `${isEpic ? t("epic") : t("issue")} ${t("created_successfully")}.`, + message: () => t("entity_created_successfully", { entity: isEpic ? t("epic") : t("issue") }), actionItems: (data) => ( = observer((props) => { )} > {isOpen ? ( - handleIsOpen(false)} - isEpic={isEpic} - /> + handleIsOpen(false)} + isEpic={isEpic} + /> ) : ( <> {QuickAddButton && handleIsOpen(true)} />} diff --git a/web/core/components/issues/peek-overview/root.tsx b/web/core/components/issues/peek-overview/root.tsx index 94d2fd1a03c..8b4ad90b459 100644 --- a/web/core/components/issues/peek-overview/root.tsx +++ b/web/core/components/issues/peek-overview/root.tsx @@ -95,9 +95,9 @@ export const IssuePeekOverview: FC = observer((props) => { path: pathname, }); setToast({ - title: "Error!", + title: t("error"), type: TOAST_TYPE.ERROR, - message: "Issue update failed", + message: t("issue_update_failed_please_try_again_later"), }); }); } @@ -184,8 +184,8 @@ export const IssuePeekOverview: FC = observer((props) => { } catch { setToast({ type: TOAST_TYPE.ERROR, - title: "Error!", - message: "Issue could not be added to the cycle. Please try again.", + title: t("error"), + message: t("issue_could_not_be_added_to_the_cycle_please_try_again"), }); captureIssueEvent({ eventName: ISSUE_UPDATED, @@ -213,8 +213,8 @@ export const IssuePeekOverview: FC = observer((props) => { } catch { setToast({ type: TOAST_TYPE.ERROR, - title: "Error!", - message: "Issue could not be added to the cycle. Please try again.", + title: t("error"), + message: t("issue_could_not_be_added_to_the_cycle_please_try_again"), }); captureIssueEvent({ eventName: ISSUE_UPDATED, @@ -231,14 +231,14 @@ export const IssuePeekOverview: FC = observer((props) => { try { const removeFromCyclePromise = issues.removeIssueFromCycle(workspaceSlug, projectId, cycleId, issueId); setPromiseToast(removeFromCyclePromise, { - loading: "Removing issue from the cycle...", + loading: t("removing_issue_from_the_cycle"), success: { - title: "Success!", - message: () => "Issue removed from the cycle successfully.", + title: t("success"), + message: () => t("issue_removed_from_the_cycle_successfully"), }, error: { - title: "Error!", - message: () => "Issue could not be removed from the cycle. Please try again.", + title: t("error"), + message: () => t("issue_could_not_be_removed_from_the_cycle_please_try_again"), }, }); await removeFromCyclePromise; @@ -294,14 +294,14 @@ export const IssuePeekOverview: FC = observer((props) => { try { const removeFromModulePromise = issues.removeIssuesFromModule(workspaceSlug, projectId, moduleId, [issueId]); setPromiseToast(removeFromModulePromise, { - loading: "Removing issue from the module...", + loading: t("removing_issue_from_the_module"), success: { - title: "Success!", - message: () => "Issue removed from the module successfully.", + title: t("success"), + message: () => t("issue_removed_from_the_module_successfully"), }, error: { - title: "Error!", - message: () => "Issue could not be removed from the module. Please try again.", + title: t("error"), + message: () => t("issue_could_not_be_removed_from_the_module_please_try_again"), }, }); await removeFromModulePromise;