feat(localization): update and enhance localization files for multiple languages

- Updated localization files for various languages, including English, German, Spanish, Portuguese, and Chinese, to ensure consistency and accuracy across the application.
- Added new keys and updated existing ones to support recent UI changes and features, particularly in project views, task lists, and admin center settings.
- Enhanced the structure of localization files to improve maintainability and facilitate future updates.
- Implemented performance optimizations in the frontend components to better handle localization data.
This commit is contained in:
chamiakJ
2025-07-28 07:19:55 +05:30
parent fc88c14b94
commit 591d348ae5
315 changed files with 9956 additions and 6116 deletions

View File

@@ -7,7 +7,13 @@ import {
EntityId,
createSelector,
} from '@reduxjs/toolkit';
import { Task, TaskManagementState, TaskGroup, TaskGrouping, getSortOrderField } from '@/types/task-management.types';
import {
Task,
TaskManagementState,
TaskGroup,
TaskGrouping,
getSortOrderField,
} from '@/types/task-management.types';
import { ITaskListColumn } from '@/types/tasks/taskList.types';
import { RootState } from '@/app/store';
import {
@@ -148,7 +154,7 @@ export const fetchTasks = createAsyncThunk(
group.tasks.map((task: any) => ({
id: task.id,
task_key: task.task_key || '',
title: (task.title && task.title.trim()) ? task.title.trim() : DEFAULT_TASK_NAME,
title: task.title && task.title.trim() ? task.title.trim() : DEFAULT_TASK_NAME,
description: task.description || '',
status: statusIdToNameMap[task.status] || 'todo',
priority: priorityIdToNameMap[task.priority] || 'medium',
@@ -254,11 +260,11 @@ export const fetchTasksV3 = createAsyncThunk(
// Ensure tasks are properly normalized
const tasks: Task[] = response.body.allTasks.map((task: any) => {
const now = new Date().toISOString();
const transformedTask = {
id: task.id,
task_key: task.task_key || task.key || '',
title: (task.title && task.title.trim()) ? task.title.trim() : DEFAULT_TASK_NAME,
title: task.title && task.title.trim() ? task.title.trim() : DEFAULT_TASK_NAME,
description: task.description || '',
status: task.status || 'todo',
priority: task.priority || 'medium',
@@ -266,18 +272,31 @@ export const fetchTasksV3 = createAsyncThunk(
progress: typeof task.complete_ratio === 'number' ? task.complete_ratio : 0,
assignees: task.assignees?.map((a: { team_member_id: string }) => a.team_member_id) || [],
assignee_names: task.assignee_names || task.names || [],
labels: task.labels?.map((l: { id: string; label_id: string; name: string; color: string; end: boolean; names: string[] }) => ({
id: l.id || l.label_id,
name: l.name,
color: l.color || '#1890ff',
end: l.end,
names: l.names,
})) || [],
all_labels: task.all_labels?.map((l: { id: string; label_id: string; name: string; color_code: string }) => ({
id: l.id || l.label_id,
name: l.name,
color_code: l.color_code || '#1890ff',
})) || [],
labels:
task.labels?.map(
(l: {
id: string;
label_id: string;
name: string;
color: string;
end: boolean;
names: string[];
}) => ({
id: l.id || l.label_id,
name: l.name,
color: l.color || '#1890ff',
end: l.end,
names: l.names,
})
) || [],
all_labels:
task.all_labels?.map(
(l: { id: string; label_id: string; name: string; color_code: string }) => ({
id: l.id || l.label_id,
name: l.name,
color_code: l.color_code || '#1890ff',
})
) || [],
dueDate: task.dueDate,
startDate: task.startDate,
timeTracking: {
@@ -305,7 +324,7 @@ export const fetchTasksV3 = createAsyncThunk(
schedule_id: task.schedule_id || null,
reporter: task.reporter || undefined,
};
return transformedTask;
});
@@ -514,10 +533,13 @@ const taskManagementSlice = createSlice({
setTasks: (state, action: PayloadAction<Task[]>) => {
const tasks = action.payload;
state.ids = tasks.map(task => task.id);
state.entities = tasks.reduce((acc, task) => {
acc[task.id] = task;
return acc;
}, {} as Record<string, Task>);
state.entities = tasks.reduce(
(acc, task) => {
acc[task.id] = task;
return acc;
},
{} as Record<string, Task>
);
},
addTask: (state, action: PayloadAction<Task>) => {
const task = action.payload;
@@ -526,11 +548,11 @@ const taskManagementSlice = createSlice({
},
addTaskToGroup: (state, action: PayloadAction<{ task: Task; groupId: string }>) => {
const { task, groupId } = action.payload;
state.ids.push(task.id);
state.entities[task.id] = task;
let group = state.groups.find(g => g.id === groupId);
// If group doesn't exist and it's "Unmapped", create it dynamically
if (!group && groupId === 'Unmapped') {
const unmappedGroup = {
@@ -539,12 +561,12 @@ const taskManagementSlice = createSlice({
taskIds: [],
type: 'phase' as const,
color: '#fbc84c69',
groupValue: 'Unmapped'
groupValue: 'Unmapped',
};
state.groups.push(unmappedGroup);
group = unmappedGroup;
}
if (group) {
group.taskIds.push(task.id);
}
@@ -554,14 +576,18 @@ const taskManagementSlice = createSlice({
// Additionally, update the task within its group if necessary (e.g., if status changed)
const updatedTask = action.payload;
const oldTask = state.entities[updatedTask.id];
if (oldTask && state.grouping?.id === IGroupBy.STATUS && oldTask.status !== updatedTask.status) {
if (
oldTask &&
state.grouping?.id === IGroupBy.STATUS &&
oldTask.status !== updatedTask.status
) {
// Remove from old status group
const oldGroup = state.groups.find(group => group.id === oldTask.status);
if (oldGroup) {
oldGroup.taskIds = oldGroup.taskIds.filter(id => id !== updatedTask.id);
}
// Add to new status group
const newGroup = state.groups.find(group => group.id === updatedTask.status);
if (newGroup) {
@@ -626,8 +652,8 @@ const taskManagementSlice = createSlice({
group.id === targetGroupId
? [...group.taskIds, taskId]
: group.id === sourceGroupId
? group.taskIds.filter(id => id !== taskId)
: group.taskIds,
? group.taskIds.filter(id => id !== taskId)
: group.taskIds,
}));
},
optimisticTaskMove: (
@@ -645,8 +671,8 @@ const taskManagementSlice = createSlice({
group.id === targetGroupId
? [...group.taskIds, taskId]
: group.id === sourceGroupId
? group.taskIds.filter(id => id !== taskId)
: group.taskIds,
? group.taskIds.filter(id => id !== taskId)
: group.taskIds,
}));
},
reorderTasksInGroup: (
@@ -659,15 +685,15 @@ const taskManagementSlice = createSlice({
}>
) => {
const { sourceTaskId, destinationTaskId, sourceGroupId, destinationGroupId } = action.payload;
// Get a mutable copy of entities for updates
const newEntities = { ...state.entities };
const sourceTask = newEntities[sourceTaskId];
const destinationTask = newEntities[destinationTaskId];
if (!sourceTask || !destinationTask) return;
if (sourceGroupId === destinationGroupId) {
// Reordering within the same group
const group = state.groups.find(g => g.id === sourceGroupId);
@@ -715,7 +741,7 @@ const taskManagementSlice = createSlice({
});
}
}
// Update the state's entities after all modifications
state.entities = newEntities;
},
@@ -734,7 +760,7 @@ const taskManagementSlice = createSlice({
setArchived: (state, action: PayloadAction<boolean>) => {
state.archived = action.payload;
},
toggleArchived: (state) => {
toggleArchived: state => {
state.archived = !state.archived;
},
resetTaskManagement: state => {
@@ -754,10 +780,7 @@ const taskManagementSlice = createSlice({
task.show_sub_tasks = !task.show_sub_tasks;
}
},
addSubtaskToParent: (
state,
action: PayloadAction<{ parentId: string; subtask: Task }>
) => {
addSubtaskToParent: (state, action: PayloadAction<{ parentId: string; subtask: Task }>) => {
const { parentId, subtask } = action.payload;
const parent = state.entities[parentId];
if (parent) {
@@ -808,7 +831,7 @@ const taskManagementSlice = createSlice({
show_sub_tasks: false,
isTemporary: true, // Mark as temporary
};
// Add temporary subtask for immediate UI feedback
if (!parent.sub_tasks) {
parent.sub_tasks = [];
@@ -832,14 +855,17 @@ const taskManagementSlice = createSlice({
state.ids = state.ids.filter(id => id !== tempId);
}
},
updateTaskAssignees: (state, action: PayloadAction<{
taskId: string;
assigneeIds: string[];
assigneeNames: InlineMember[];
}>) => {
updateTaskAssignees: (
state,
action: PayloadAction<{
taskId: string;
assigneeIds: string[];
assigneeNames: InlineMember[];
}>
) => {
const { taskId, assigneeIds, assigneeNames } = action.payload;
const existingTask = state.entities[taskId];
if (existingTask) {
state.entities[taskId] = {
...existingTask,
@@ -893,23 +919,26 @@ const taskManagementSlice = createSlice({
if (field) {
return {
...column,
pinned: field.visible
pinned: field.visible,
};
}
return column;
});
},
// Add action to update task counts (comments, attachments, etc.)
updateTaskCounts: (state, action: PayloadAction<{
taskId: string;
counts: {
comments_count?: number;
attachments_count?: number;
has_subscribers?: boolean;
has_dependencies?: boolean;
schedule_id?: string | null; // Add schedule_id for recurring tasks
};
}>) => {
updateTaskCounts: (
state,
action: PayloadAction<{
taskId: string;
counts: {
comments_count?: number;
attachments_count?: number;
has_subscribers?: boolean;
has_dependencies?: boolean;
schedule_id?: string | null; // Add schedule_id for recurring tasks
};
}>
) => {
const { taskId, counts } = action.payload;
const task = state.entities[taskId];
if (task) {
@@ -941,7 +970,7 @@ const taskManagementSlice = createSlice({
.addCase(fetchTasksV3.fulfilled, (state, action) => {
state.loading = false;
const { allTasks, groups, grouping } = action.payload;
// Preserve existing timer state from old tasks before replacing
const oldTasks = state.entities;
const tasksWithTimers = (allTasks || []).map(task => {
@@ -952,13 +981,13 @@ const taskManagementSlice = createSlice({
...task,
timeTracking: {
...task.timeTracking,
activeTimer: oldTask.timeTracking.activeTimer
}
activeTimer: oldTask.timeTracking.activeTimer,
},
};
}
return task;
});
tasksAdapter.setAll(state as EntityState<Task, string>, tasksWithTimers); // Ensure allTasks is an array
state.ids = tasksWithTimers.map(task => task.id); // Also update ids
state.groups = groups;
@@ -966,7 +995,8 @@ const taskManagementSlice = createSlice({
})
.addCase(fetchTasksV3.rejected, (state, action) => {
state.loading = false;
state.error = action.error?.message || (action.payload as string) || 'Failed to load tasks (V3)';
state.error =
action.error?.message || (action.payload as string) || 'Failed to load tasks (V3)';
state.ids = [];
state.entities = {};
state.groups = [];
@@ -1018,7 +1048,7 @@ const taskManagementSlice = createSlice({
// Update parent task with subtasks
parentTask.sub_tasks = convertedSubtasks;
parentTask.sub_tasks_count = convertedSubtasks.length;
// Add subtasks to entities so they can be accessed by ID
convertedSubtasks.forEach(subtask => {
state.entities[subtask.id] = subtask;
@@ -1032,9 +1062,10 @@ const taskManagementSlice = createSlice({
// Clear loading state and set error
const { taskId } = action.meta.arg;
state.loadingSubtasks[taskId] = false;
state.error = action.error.message || action.payload || 'Failed to fetch subtasks. Please try again.';
state.error =
action.error.message || action.payload || 'Failed to fetch subtasks. Please try again.';
})
.addCase(fetchTasks.pending, (state) => {
.addCase(fetchTasks.pending, state => {
state.loading = true;
state.error = null;
})
@@ -1148,19 +1179,21 @@ export const {
export const selectAllTasks = (state: RootState) => state.taskManagement.entities;
// Memoized selector to prevent unnecessary re-renders
export const selectAllTasksArray = createSelector(
[selectAllTasks],
(entities) => Object.values(entities)
export const selectAllTasksArray = createSelector([selectAllTasks], entities =>
Object.values(entities)
);
export const selectTaskById = (state: RootState, taskId: string) => state.taskManagement.entities[taskId];
export const selectTaskById = (state: RootState, taskId: string) =>
state.taskManagement.entities[taskId];
export const selectTaskIds = (state: RootState) => state.taskManagement.ids;
export const selectGroups = (state: RootState) => state.taskManagement.groups;
export const selectGrouping = (state: RootState) => state.taskManagement.grouping;
export const selectLoading = (state: RootState) => state.taskManagement.loading;
export const selectError = (state: RootState) => state.taskManagement.error;
export const selectSelectedPriorities = (state: RootState) => state.taskManagement.selectedPriorities;
export const selectSelectedPriorities = (state: RootState) =>
state.taskManagement.selectedPriorities;
export const selectSearch = (state: RootState) => state.taskManagement.search;
export const selectSubtaskLoading = (state: RootState, taskId: string) => state.taskManagement.loadingSubtasks[taskId] || false;
export const selectSubtaskLoading = (state: RootState, taskId: string) =>
state.taskManagement.loadingSubtasks[taskId] || false;
// Memoized selectors to prevent unnecessary re-renders
export const selectTasksByStatus = createSelector(
@@ -1197,9 +1230,9 @@ export const selectLoadingColumns = (state: RootState) => state.taskManagement.l
export const selectColumnsInSync = (state: RootState) => {
const columns = state.taskManagement.columns;
const fields = state.taskManagementFields || [];
if (columns.length === 0 || fields.length === 0) return true;
return !fields.some(field => {
const backendColumn = columns.find(c => c.key === field.key);
if (backendColumn) {