From ab7ca33ac10aef27ed56009a807b2b25c3e5b23b Mon Sep 17 00:00:00 2001 From: chamikaJ Date: Wed, 9 Jul 2025 11:58:40 +0530 Subject: [PATCH] feat(localization): improve translation handling and update UI labels - Updated the 'board' label in project-view translations for consistency. - Enhanced the getTabLabel function to include fallback labels for better user experience when translations are not available. - Implemented translation loading checks in ProjectView to ensure labels are updated correctly based on the selected language. - Refactored tab label updates to handle translation loading errors gracefully. --- .../public/locales/en/project-view.json | 2 +- .../src/lib/project/project-view-constants.ts | 79 +++++++++++++------ .../projects/projectView/project-view.tsx | 49 +++++++++--- 3 files changed, 93 insertions(+), 37 deletions(-) diff --git a/worklenz-frontend/public/locales/en/project-view.json b/worklenz-frontend/public/locales/en/project-view.json index 16d2a0bc..82ab21f2 100644 --- a/worklenz-frontend/public/locales/en/project-view.json +++ b/worklenz-frontend/public/locales/en/project-view.json @@ -1,6 +1,6 @@ { "taskList": "Task List", - "board": "Kanban Board", + "board": "Board", "insights": "Insights", "files": "Files", "members": "Members", diff --git a/worklenz-frontend/src/lib/project/project-view-constants.ts b/worklenz-frontend/src/lib/project/project-view-constants.ts index 289b98c5..c6b71a79 100644 --- a/worklenz-frontend/src/lib/project/project-view-constants.ts +++ b/worklenz-frontend/src/lib/project/project-view-constants.ts @@ -29,9 +29,36 @@ type TabItems = { element: ReactNode; }; -// Function to get translated labels +// Function to get translated labels with fallback const getTabLabel = (key: string): string => { - return i18n.t(`project-view:${key}`); + try { + const translated = i18n.t(`project-view:${key}`); + // If translation is not loaded, it returns the key back, so we provide fallbacks + if (translated === `project-view:${key}` || translated === key) { + // Provide fallback labels + const fallbacks: Record = { + taskList: 'Task List', + board: 'Board', + insights: 'Insights', + files: 'Files', + members: 'Members', + updates: 'Updates', + }; + return fallbacks[key] || key; + } + return translated; + } catch (error) { + // Fallback labels in case of any error + const fallbacks: Record = { + taskList: 'Task List', + board: 'Board', + insights: 'Insights', + files: 'Files', + members: 'Members', + updates: 'Updates', + }; + return fallbacks[key] || key; + } }; // settings all element items use for tabs @@ -94,26 +121,30 @@ export const tabItems: TabItems[] = [ // Function to update tab labels when language changes export const updateTabLabels = () => { - tabItems.forEach(item => { - switch (item.key) { - case 'tasks-list': - item.label = getTabLabel('taskList'); - break; - case 'board': - item.label = getTabLabel('board'); - break; - case 'project-insights-member-overview': - item.label = getTabLabel('insights'); - break; - case 'all-attachments': - item.label = getTabLabel('files'); - break; - case 'members': - item.label = getTabLabel('members'); - break; - case 'updates': - item.label = getTabLabel('updates'); - break; - } - }); + try { + tabItems.forEach(item => { + switch (item.key) { + case 'tasks-list': + item.label = getTabLabel('taskList'); + break; + case 'board': + item.label = getTabLabel('board'); + break; + case 'project-insights-member-overview': + item.label = getTabLabel('insights'); + break; + case 'all-attachments': + item.label = getTabLabel('files'); + break; + case 'members': + item.label = getTabLabel('members'); + break; + case 'updates': + item.label = getTabLabel('updates'); + break; + } + }); + } catch (error) { + console.error('Error updating tab labels:', error); + } }; diff --git a/worklenz-frontend/src/pages/projects/projectView/project-view.tsx b/worklenz-frontend/src/pages/projects/projectView/project-view.tsx index 7509d74b..c0228007 100644 --- a/worklenz-frontend/src/pages/projects/projectView/project-view.tsx +++ b/worklenz-frontend/src/pages/projects/projectView/project-view.tsx @@ -7,8 +7,6 @@ import { Button, ConfigProvider, Flex, - Tooltip, - Badge, Tabs, PushpinFilled, PushpinOutlined, @@ -20,7 +18,6 @@ import { useAppSelector } from '@/hooks/useAppSelector'; import { getProject, setProjectId, setProjectView } from '@/features/project/project.slice'; import { fetchStatuses, resetStatuses } from '@/features/taskAttributes/taskStatusSlice'; import { projectsApiService } from '@/api/projects/projects.api.service'; -import { colors } from '@/styles/colors'; import { useDocumentTitle } from '@/hooks/useDoumentTItle'; import ProjectViewHeader from './project-view-header'; import './project-view.css'; @@ -42,6 +39,7 @@ import { resetState as resetEnhancedKanbanState } from '@/features/enhanced-kanb import { setProjectId as setInsightsProjectId } from '@/features/projects/insights/project-insights.slice'; import { SuspenseFallback } from '@/components/suspense-fallback/suspense-fallback'; import { useTranslation } from 'react-i18next'; +import { ensureTranslationsLoaded } from '@/i18n'; // Import critical components synchronously to avoid suspense interruptions import TaskDrawer from '@components/task-drawer/task-drawer'; @@ -64,12 +62,15 @@ const ProjectView = React.memo(() => { const dispatch = useAppDispatch(); const [searchParams] = useSearchParams(); const { projectId } = useParams(); - const { t } = useTranslation('project-view'); + const { t, i18n } = useTranslation('project-view'); // Memoized selectors to prevent unnecessary re-renders const selectedProject = useAppSelector(state => state.projectReducer.project); const projectLoading = useAppSelector(state => state.projectReducer.projectLoading); + // State to track translation loading + const [translationsReady, setTranslationsReady] = useState(false); + // Optimize document title updates useDocumentTitle(selectedProject?.name || t('projectView')); @@ -95,6 +96,30 @@ const ProjectView = React.memo(() => { setTaskId(urlParams.taskId); }, [urlParams]); + // Ensure translations are loaded for project-view namespace + useEffect(() => { + const loadTranslations = async () => { + try { + await ensureTranslationsLoaded(['project-view'], [i18n.language]); + updateTabLabels(); + setTranslationsReady(true); + } catch (error) { + console.error('Failed to load project-view translations:', error); + // Set ready to true anyway to prevent infinite loading + setTranslationsReady(true); + } + }; + + loadTranslations(); + }, [i18n.language]); + + // Update tab labels when language changes + useEffect(() => { + if (translationsReady) { + updateTabLabels(); + } + }, [t, translationsReady]); + // Comprehensive cleanup function for when leaving project view entirely const resetAllProjectData = useCallback(() => { dispatch(setProjectId(null)); @@ -176,11 +201,6 @@ const ProjectView = React.memo(() => { setIsInitialized(false); }, [projectId]); - // Update tab labels when language changes - useEffect(() => { - updateTabLabels(); - }, [t]); - // Effect for handling task drawer opening from URL params useEffect(() => { if (taskid && isInitialized) { @@ -250,6 +270,11 @@ const ProjectView = React.memo(() => { // Memoized tab menu items with enhanced styling const tabMenuItems = useMemo(() => { + // Only render tabs when translations are ready + if (!translationsReady) { + return []; + } + const menuItems = tabItems.map(item => ({ key: item.key, label: ( @@ -304,7 +329,7 @@ const ProjectView = React.memo(() => { })); return menuItems; - }, [pinnedTab, pinToDefaultTab, t]); + }, [pinnedTab, pinToDefaultTab, t, translationsReady]); // Optimized secondary components loading with better UX const [shouldLoadSecondaryComponents, setShouldLoadSecondaryComponents] = useState(false); @@ -341,8 +366,8 @@ const ProjectView = React.memo(() => { [shouldLoadSecondaryComponents] ); - // Show loading state while project is being fetched - if (projectLoading || !isInitialized) { + // Show loading state while project is being fetched or translations are loading + if (projectLoading || !isInitialized || !translationsReady) { return (