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.
This commit is contained in:
chamikaJ
2025-07-09 11:58:40 +05:30
parent bc6a15de8f
commit ab7ca33ac1
3 changed files with 93 additions and 37 deletions

View File

@@ -1,6 +1,6 @@
{ {
"taskList": "Task List", "taskList": "Task List",
"board": "Kanban Board", "board": "Board",
"insights": "Insights", "insights": "Insights",
"files": "Files", "files": "Files",
"members": "Members", "members": "Members",

View File

@@ -29,9 +29,36 @@ type TabItems = {
element: ReactNode; element: ReactNode;
}; };
// Function to get translated labels // Function to get translated labels with fallback
const getTabLabel = (key: string): string => { 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<string, string> = {
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<string, string> = {
taskList: 'Task List',
board: 'Board',
insights: 'Insights',
files: 'Files',
members: 'Members',
updates: 'Updates',
};
return fallbacks[key] || key;
}
}; };
// settings all element items use for tabs // settings all element items use for tabs
@@ -94,26 +121,30 @@ export const tabItems: TabItems[] = [
// Function to update tab labels when language changes // Function to update tab labels when language changes
export const updateTabLabels = () => { export const updateTabLabels = () => {
tabItems.forEach(item => { try {
switch (item.key) { tabItems.forEach(item => {
case 'tasks-list': switch (item.key) {
item.label = getTabLabel('taskList'); case 'tasks-list':
break; item.label = getTabLabel('taskList');
case 'board': break;
item.label = getTabLabel('board'); case 'board':
break; item.label = getTabLabel('board');
case 'project-insights-member-overview': break;
item.label = getTabLabel('insights'); case 'project-insights-member-overview':
break; item.label = getTabLabel('insights');
case 'all-attachments': break;
item.label = getTabLabel('files'); case 'all-attachments':
break; item.label = getTabLabel('files');
case 'members': break;
item.label = getTabLabel('members'); case 'members':
break; item.label = getTabLabel('members');
case 'updates': break;
item.label = getTabLabel('updates'); case 'updates':
break; item.label = getTabLabel('updates');
} break;
}); }
});
} catch (error) {
console.error('Error updating tab labels:', error);
}
}; };

View File

@@ -7,8 +7,6 @@ import {
Button, Button,
ConfigProvider, ConfigProvider,
Flex, Flex,
Tooltip,
Badge,
Tabs, Tabs,
PushpinFilled, PushpinFilled,
PushpinOutlined, PushpinOutlined,
@@ -20,7 +18,6 @@ import { useAppSelector } from '@/hooks/useAppSelector';
import { getProject, setProjectId, setProjectView } from '@/features/project/project.slice'; import { getProject, setProjectId, setProjectView } from '@/features/project/project.slice';
import { fetchStatuses, resetStatuses } from '@/features/taskAttributes/taskStatusSlice'; import { fetchStatuses, resetStatuses } from '@/features/taskAttributes/taskStatusSlice';
import { projectsApiService } from '@/api/projects/projects.api.service'; import { projectsApiService } from '@/api/projects/projects.api.service';
import { colors } from '@/styles/colors';
import { useDocumentTitle } from '@/hooks/useDoumentTItle'; import { useDocumentTitle } from '@/hooks/useDoumentTItle';
import ProjectViewHeader from './project-view-header'; import ProjectViewHeader from './project-view-header';
import './project-view.css'; 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 { setProjectId as setInsightsProjectId } from '@/features/projects/insights/project-insights.slice';
import { SuspenseFallback } from '@/components/suspense-fallback/suspense-fallback'; import { SuspenseFallback } from '@/components/suspense-fallback/suspense-fallback';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ensureTranslationsLoaded } from '@/i18n';
// Import critical components synchronously to avoid suspense interruptions // Import critical components synchronously to avoid suspense interruptions
import TaskDrawer from '@components/task-drawer/task-drawer'; import TaskDrawer from '@components/task-drawer/task-drawer';
@@ -64,12 +62,15 @@ const ProjectView = React.memo(() => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { projectId } = useParams(); const { projectId } = useParams();
const { t } = useTranslation('project-view'); const { t, i18n } = useTranslation('project-view');
// Memoized selectors to prevent unnecessary re-renders // Memoized selectors to prevent unnecessary re-renders
const selectedProject = useAppSelector(state => state.projectReducer.project); const selectedProject = useAppSelector(state => state.projectReducer.project);
const projectLoading = useAppSelector(state => state.projectReducer.projectLoading); const projectLoading = useAppSelector(state => state.projectReducer.projectLoading);
// State to track translation loading
const [translationsReady, setTranslationsReady] = useState(false);
// Optimize document title updates // Optimize document title updates
useDocumentTitle(selectedProject?.name || t('projectView')); useDocumentTitle(selectedProject?.name || t('projectView'));
@@ -95,6 +96,30 @@ const ProjectView = React.memo(() => {
setTaskId(urlParams.taskId); setTaskId(urlParams.taskId);
}, [urlParams]); }, [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 // Comprehensive cleanup function for when leaving project view entirely
const resetAllProjectData = useCallback(() => { const resetAllProjectData = useCallback(() => {
dispatch(setProjectId(null)); dispatch(setProjectId(null));
@@ -176,11 +201,6 @@ const ProjectView = React.memo(() => {
setIsInitialized(false); setIsInitialized(false);
}, [projectId]); }, [projectId]);
// Update tab labels when language changes
useEffect(() => {
updateTabLabels();
}, [t]);
// Effect for handling task drawer opening from URL params // Effect for handling task drawer opening from URL params
useEffect(() => { useEffect(() => {
if (taskid && isInitialized) { if (taskid && isInitialized) {
@@ -250,6 +270,11 @@ const ProjectView = React.memo(() => {
// Memoized tab menu items with enhanced styling // Memoized tab menu items with enhanced styling
const tabMenuItems = useMemo(() => { const tabMenuItems = useMemo(() => {
// Only render tabs when translations are ready
if (!translationsReady) {
return [];
}
const menuItems = tabItems.map(item => ({ const menuItems = tabItems.map(item => ({
key: item.key, key: item.key,
label: ( label: (
@@ -304,7 +329,7 @@ const ProjectView = React.memo(() => {
})); }));
return menuItems; return menuItems;
}, [pinnedTab, pinToDefaultTab, t]); }, [pinnedTab, pinToDefaultTab, t, translationsReady]);
// Optimized secondary components loading with better UX // Optimized secondary components loading with better UX
const [shouldLoadSecondaryComponents, setShouldLoadSecondaryComponents] = useState(false); const [shouldLoadSecondaryComponents, setShouldLoadSecondaryComponents] = useState(false);
@@ -341,8 +366,8 @@ const ProjectView = React.memo(() => {
[shouldLoadSecondaryComponents] [shouldLoadSecondaryComponents]
); );
// Show loading state while project is being fetched // Show loading state while project is being fetched or translations are loading
if (projectLoading || !isInitialized) { if (projectLoading || !isInitialized || !translationsReady) {
return ( return (
<div style={{ marginBlockStart: 70, marginBlockEnd: 12, minHeight: '80vh' }}> <div style={{ marginBlockStart: 70, marginBlockEnd: 12, minHeight: '80vh' }}>
<SuspenseFallback /> <SuspenseFallback />