feat(i18n): Implement multilingual support with Polish and English translations

- Added translation context and provider for managing language state.
- Integrated translation functionality into existing components (TaskStatusDropdown, Navigation).
- Created LanguageSwitcher component for language selection.
- Updated task statuses and navigation labels to use translations.
- Added Polish translations for various UI elements, including navigation, tasks, projects, and contracts.
- Refactored utility functions to return localized strings for deadlines and date formatting.
This commit is contained in:
Chop
2025-07-27 22:01:15 +02:00
parent 9b6307eabe
commit e828aa660b
16 changed files with 1166 additions and 234 deletions

859
src/lib/i18n.js Normal file
View File

@@ -0,0 +1,859 @@
"use client";
import { createContext, useContext, useState, useEffect } from 'react';
// Translation context
const TranslationContext = createContext();
// Translation data
const translations = {
pl: {
// Navigation
navigation: {
dashboard: "Panel główny",
projects: "Projekty",
taskTemplates: "Szablony zadań",
projectTasks: "Zadania projektów",
contracts: "Umowy",
userManagement: "Zarządzanie użytkownikami",
projectPanel: "Panel Projektów",
loading: "Ładowanie...",
signOut: "Wyloguj się",
signIn: "Zaloguj się"
},
// Common UI elements
common: {
save: "Zapisz",
cancel: "Anuluj",
delete: "Usuń",
edit: "Edytuj",
add: "Dodaj",
create: "Utwórz",
update: "Aktualizuj",
search: "Szukaj",
filter: "Filtruj",
loading: "Ładowanie...",
saving: "Zapisywanie...",
creating: "Tworzenie...",
updating: "Aktualizowanie...",
yes: "Tak",
no: "Nie",
confirm: "Potwierdź",
close: "Zamknij",
back: "Wstecz",
next: "Dalej",
previous: "Poprzedni",
page: "Strona",
of: "z",
items: "pozycji",
all: "Wszystkie",
none: "Brak",
required: "wymagane",
optional: "opcjonalne",
selectOption: "Wybierz opcję",
selectLanguage: "Wybierz język",
noResults: "Brak wyników",
error: "Błąd",
success: "Sukces",
warning: "Ostrzeżenie",
info: "Informacja",
deleteConfirm: "Czy na pewno chcesz to usunąć?",
addNote: "Dodaj notatkę",
addNoteSuccess: "Notatka została dodana",
addNoteError: "Nie udało się zapisać notatki",
addNotePlaceholder: "Dodaj nową notatkę..."
},
// Dashboard
dashboard: {
title: "Panel główny",
subtitle: "Przegląd twoich projektów i zadań",
totalProjects: "Wszystkie projekty",
activeProjects: "Aktywne projekty",
completedProjects: "Ukończone projekty",
overdueProjects: "Przeterminowane projekty",
totalContracts: "Wszystkie umowy",
activeContracts: "Aktywne umowy",
pendingTasks: "Oczekujące zadania",
inProgressTasks: "Zadania w trakcie",
completedTasks: "Ukończone zadania",
overdueTasks: "Przeterminowane zadania",
recentProjects: "Najnowsze projekty",
recentTasks: "Najnowsze zadania",
upcomingDeadlines: "Nadchodzące terminy",
projectsThisWeek: "Projekty w tym tygodniu",
tasksThisWeek: "Zadania w tym tygodniu",
completionRate: "Wskaźnik ukończenia",
viewAll: "Zobacz wszystkie",
noRecentProjects: "Brak najnowszych projektów",
noRecentTasks: "Brak najnowszych zadań",
noUpcomingDeadlines: "Brak nadchodzących terminów"
},
// Project statuses
projectStatus: {
registered: "Zarejestrowany",
in_progress_design: "W realizacji (projektowanie)",
in_progress_construction: "W realizacji (realizacja)",
fulfilled: "Zakończony",
unknown: "Nieznany"
},
// Project types
projectType: {
design: "Projektowanie",
construction: "Realizacja",
"design+construction": "Projektowanie + Realizacja"
},
// Task statuses
taskStatus: {
pending: "Oczekujące",
in_progress: "W trakcie",
completed: "Ukończone",
cancelled: "Anulowane",
on_hold: "Wstrzymane"
},
// Projects
projects: {
title: "Projekty",
subtitle: "Zarządzaj swoimi projektami",
newProject: "Nowy projekt",
editProject: "Edytuj projekt",
deleteProject: "Usuń projekt",
projectName: "Nazwa projektu",
city: "Miasto",
address: "Adres",
plot: "Działka",
district: "Dzielnica",
unit: "Jednostka",
finishDate: "Data zakończenia",
contact: "Kontakt",
coordinates: "Współrzędne",
notes: "Notatki",
assignedTo: "Przypisane do",
unassigned: "Nieprzypisane",
createdBy: "Utworzone przez",
lastModified: "Ostatnia modyfikacja",
basicInformation: "Informacje podstawowe",
locationDetails: "Szczegóły lokalizacji",
projectDetails: "Szczegóły projektu",
additionalInfo: "Dodatkowe informacje",
searchPlaceholder: "Szukaj projektów po nazwie, mieście lub adresie...",
noProjects: "Brak projektów",
noProjectsMessage: "Rozpocznij od utworzenia swojego pierwszego projektu.",
deleteConfirm: "Czy na pewno chcesz usunąć ten projekt?",
deleteSuccess: "Projekt został pomyślnie usunięty",
createSuccess: "Projekt został pomyślnie utworzony",
updateSuccess: "Projekt został pomyślnie zaktualizowany",
enterProjectName: "Wprowadź nazwę projektu",
enterCity: "Wprowadź miasto",
enterAddress: "Wprowadź adres",
enterPlotNumber: "Wprowadź numer działki",
enterDistrict: "Wprowadź dzielnicę",
enterUnit: "Wprowadź jednostkę",
enterContactDetails: "Wprowadź dane kontaktowe",
coordinatesPlaceholder: "np. 49.622958,20.629562",
enterNotes: "Wprowadź notatki..."
},
// Contracts
contracts: {
title: "Umowy",
subtitle: "Zarządzaj swoimi umowami i kontraktami",
newContract: "Nowa umowa",
editContract: "Edytuj umowę",
deleteContract: "Usuń umowę",
contractNumber: "Numer umowy",
contractName: "Nazwa umowy",
customerContractNumber: "Numer umowy klienta",
customer: "Klient",
investor: "Inwestor",
dateSigned: "Data zawarcia",
finishDate: "Data zakończenia",
searchPlaceholder: "Szukaj umów po numerze, nazwie, kliencie lub inwestorze...",
noContracts: "Brak umów",
noContractsMessage: "Rozpocznij od utworzenia swojej pierwszej umowy.",
noMatchingContracts: "Brak pasujących umów",
changeSearchCriteria: "Spróbuj zmienić kryteria wyszukiwania lub filtry.",
all: "Wszystkie",
active: "Aktywne",
withoutEndDate: "W trakcie",
expired: "Przeterminowane",
createContract: "Utwórz umowę",
enterContractNumber: "Wprowadź numer umowy",
enterContractName: "Wprowadź nazwę umowy",
enterCustomerContractNumber: "Wprowadź numer umowy klienta",
enterCustomerName: "Wprowadź nazwę klienta",
enterInvestorName: "Wprowadź nazwę inwestora",
signedOn: "Zawarcie:",
finishOn: "Zakończenie:",
customerLabel: "Zleceniodawca:",
investorLabel: "Inwestor:"
},
// Tasks
tasks: {
title: "Zadania",
task: "zadanie",
tasks: "zadania",
subtitle: "Zarządzaj zadaniami projektów",
newTask: "Nowe zadanie",
editTask: "Edytuj zadanie",
deleteTask: "Usuń zadanie",
taskName: "Nazwa zadania",
description: "Opis",
priority: "Priorytet",
assignedTo: "Przypisane do",
dueDate: "Termin wykonania",
status: "Status",
project: "Projekt",
template: "Szablon",
searchPlaceholder: "Szukaj zadań...",
noTasks: "Brak zadań",
noTasksMessage: "Brak zadań przypisanych do tego projektu.",
addTaskMessage: "Dodaj zadanie powyżej, aby rozpocząć.",
filterBy: "Filtruj według",
sortBy: "Sortuj według",
allTasks: "Wszystkie zadania",
myTasks: "Moje zadania",
overdue: "Przeterminowane",
dueSoon: "Niedługo termin",
high: "Wysoki",
normal: "Normalny",
medium: "Średni",
low: "Niski",
dateCreated: "Data utworzenia",
dateModified: "Data modyfikacji",
daysLeft: "dni pozostało",
daysOverdue: "dni przeterminowane",
dueToday: "Termin dzisiaj",
startTask: "Rozpocznij zadanie",
completeTask: "Zakończ zadanie",
comments: "Komentarze",
addComment: "Dodaj komentarz",
noComments: "Brak komentarzy",
maxWait: "Maksymalne oczekiwanie",
dateStarted: "Data rozpoczęcia",
actions: "Działania",
urgent: "Pilne",
updateTask: "Aktualizuj zadanie",
notStarted: "Nie rozpoczęte",
days: "dni"
},
// Task Templates
taskTemplates: {
title: "Szablony zadań",
subtitle: "Zarządzaj szablonami zadań",
newTemplate: "Nowy szablon",
editTemplate: "Edytuj szablon",
deleteTemplate: "Usuń szablon",
templateName: "Nazwa szablonu",
templateDescription: "Opis szablonu",
defaultPriority: "Domyślny priorytet",
estimatedDuration: "Szacowany czas trwania",
category: "Kategoria",
searchPlaceholder: "Szukaj szablonów...",
noTemplates: "Brak szablonów",
noTemplatesMessage: "Rozpocznij od utworzenia swojego pierwszego szablonu zadania.",
useTemplate: "Użyj szablonu",
duplicateTemplate: "Duplikuj szablon"
},
// Forms
forms: {
validation: {
required: "To pole jest wymagane",
email: "Wprowadź prawidłowy adres email",
minLength: "Minimalna długość: {min} znaków",
maxLength: "Maksymalna długość: {max} znaków",
invalidDate: "Nieprawidłowa data",
invalidCoordinates: "Nieprawidłowe współrzędne"
},
placeholders: {
enterText: "Wprowadź tekst...",
selectDate: "Wybierz datę",
selectOption: "Wybierz opcję",
searchResults: "Szukaj wyników..."
}
},
// Sorting and filtering
sorting: {
sortBy: "Sortuj według",
orderBy: "Kolejność",
ascending: "Rosnąco",
descending: "Malejąco",
name: "Nazwa",
date: "Data",
status: "Status",
priority: "Priorytet",
dateCreated: "Data utworzenia",
dateModified: "Data modyfikacji",
startDate: "Data rozpoczęcia",
finishDate: "Data zakończenia"
},
// Date formats
dates: {
today: "Dzisiaj",
yesterday: "Wczoraj",
tomorrow: "Jutro",
daysAgo: "{count} dni temu",
inDays: "za {count} dni",
invalidDate: "Nieprawidłowa data",
selectDate: "Wybierz datę"
},
// Map layers
mapLayers: {
polishOrthophoto: "🛰️ Polska Ortofotomapa",
polishCadastral: "📋 Polskie Dane Katastralne",
polishSpatialPlanning: "🏗️ Polskie Planowanie Przestrzenne",
lpPortalRoads: "🛣️ LP-Portal Drogi",
lpPortalStreetNames: "🏷️ LP-Portal Nazwy Ulic",
lpPortalParcels: "📐 LP-Portal Działki",
lpPortalSurveyMarkers: "📍 LP-Portal Punkty Pomiarowe",
openStreetMap: "🗺️ OpenStreetMap",
googleSatellite: "🛰️ Google Satelita",
googleRoads: "🛣️ Google Drogi",
layerControl: "Kontrola warstw",
baseLayers: "Warstwy bazowe",
overlayLayers: "Warstwy nakładkowe",
opacity: "Przezroczystość"
},
// Authentication
auth: {
signIn: "Zaloguj się",
signOut: "Wyloguj się",
email: "Email",
password: "Hasło",
rememberMe: "Zapamiętaj mnie",
forgotPassword: "Zapomniałeś hasła?",
signInWith: "Zaloguj się przez",
signUp: "Zarejestruj się",
createAccount: "Utwórz konto",
alreadyHaveAccount: "Masz już konto?",
dontHaveAccount: "Nie masz konta?",
signInError: "Błąd logowania",
signOutError: "Błąd wylogowania",
emailRequired: "Email jest wymagany",
passwordRequired: "Hasło jest wymagane",
invalidCredentials: "Nieprawidłowe dane logowania"
},
// User roles
userRoles: {
admin: "Administrator",
user: "Użytkownik",
manager: "Menedżer",
viewer: "Czytelnik"
},
// Error messages
errors: {
generic: "Wystąpił błąd. Spróbuj ponownie.",
network: "Błąd połączenia sieciowego",
unauthorized: "Brak autoryzacji",
forbidden: "Brak uprawnień",
notFound: "Nie znaleziono",
validation: "Błąd walidacji danych",
server: "Błąd serwera",
timeout: "Przekroczono limit czasu",
unknown: "Nieznany błąd"
},
// Success messages
success: {
saved: "Zapisano pomyślnie",
created: "Utworzono pomyślnie",
updated: "Zaktualizowano pomyślnie",
deleted: "Usunięto pomyślnie",
sent: "Wysłano pomyślnie",
completed: "Ukończono pomyślnie"
},
// Admin section
admin: {
title: "Administracja",
subtitle: "Zarządzaj użytkownikami systemu i uprawnieniami",
userManagement: "Zarządzanie użytkownikami",
auditLogs: "Logi audytu",
systemSettings: "Ustawienia systemu",
users: "Użytkownicy",
newUser: "Nowy użytkownik",
editUser: "Edytuj użytkownika",
deleteUser: "Usuń użytkownika",
userName: "Nazwa użytkownika",
userEmail: "Email użytkownika",
userRole: "Rola użytkownika",
active: "Aktywny",
inactive: "Nieaktywny",
lastLogin: "Ostatnie logowanie",
createdAt: "Data utworzenia",
noUsers: "Brak użytkowników",
system: "System"
}
},
en: {
// English translations (fallback)
navigation: {
dashboard: "Dashboard",
projects: "Projects",
taskTemplates: "Task Templates",
projectTasks: "Project Tasks",
contracts: "Contracts",
userManagement: "User Management",
projectPanel: "Project Panel",
loading: "Loading...",
signOut: "Sign Out",
signIn: "Sign In"
},
common: {
save: "Save",
cancel: "Cancel",
delete: "Delete",
edit: "Edit",
add: "Add",
create: "Create",
update: "Update",
search: "Search",
filter: "Filter",
loading: "Loading...",
saving: "Saving...",
creating: "Creating...",
updating: "Updating...",
yes: "Yes",
no: "No",
confirm: "Confirm",
close: "Close",
back: "Back",
next: "Next",
previous: "Previous",
page: "Page",
of: "of",
items: "items",
all: "All",
none: "None",
required: "required",
optional: "optional",
selectOption: "Select option",
selectLanguage: "Select language",
noResults: "No results",
error: "Error",
success: "Success",
warning: "Warning",
info: "Info",
deleteConfirm: "Are you sure you want to delete this?",
addNote: "Add Note",
addNoteSuccess: "Note added",
addNoteError: "Failed to save note",
addNotePlaceholder: "Add a new note..."
},
dashboard: {
title: "Dashboard",
subtitle: "Overview of your projects and tasks",
totalProjects: "Total Projects",
activeProjects: "Active Projects",
completedProjects: "Completed Projects",
overdueProjects: "Overdue Projects",
totalContracts: "Total Contracts",
activeContracts: "Active Contracts",
pendingTasks: "Pending Tasks",
inProgressTasks: "In Progress Tasks",
completedTasks: "Completed Tasks",
overdueTasks: "Overdue Tasks",
recentProjects: "Recent Projects",
recentTasks: "Recent Tasks",
upcomingDeadlines: "Upcoming Deadlines",
projectsThisWeek: "Projects This Week",
tasksThisWeek: "Tasks This Week",
completionRate: "Completion Rate",
viewAll: "View All",
noRecentProjects: "No recent projects",
noRecentTasks: "No recent tasks",
noUpcomingDeadlines: "No upcoming deadlines"
},
projectStatus: {
registered: "Registered",
in_progress_design: "In Progress (Design)",
in_progress_construction: "In Progress (Construction)",
fulfilled: "Completed",
unknown: "Unknown"
},
projectType: {
design: "Design",
construction: "Construction",
"design+construction": "Design + Construction"
},
taskStatus: {
pending: "Pending",
in_progress: "In Progress",
completed: "Completed",
cancelled: "Cancelled",
on_hold: "On Hold"
},
projects: {
title: "Projects",
subtitle: "Manage your projects",
newProject: "New Project",
editProject: "Edit Project",
deleteProject: "Delete Project",
projectName: "Project Name",
city: "City",
address: "Address",
plot: "Plot",
district: "District",
unit: "Unit",
finishDate: "Finish Date",
contact: "Contact",
coordinates: "Coordinates",
notes: "Notes",
assignedTo: "Assigned To",
unassigned: "Unassigned",
createdBy: "Created By",
lastModified: "Last Modified",
basicInformation: "Basic Information",
locationDetails: "Location Details",
projectDetails: "Project Details",
additionalInfo: "Additional Information",
searchPlaceholder: "Search projects by name, city or address...",
noProjects: "No projects",
noProjectsMessage: "Get started by creating your first project.",
deleteConfirm: "Are you sure you want to delete this project?",
deleteSuccess: "Project deleted successfully",
createSuccess: "Project created successfully",
updateSuccess: "Project updated successfully",
enterProjectName: "Enter project name",
enterCity: "Enter city",
enterAddress: "Enter address",
enterPlotNumber: "Enter plot number",
enterDistrict: "Enter district",
enterUnit: "Enter unit",
enterContactDetails: "Enter contact details",
coordinatesPlaceholder: "e.g., 49.622958,20.629562",
enterNotes: "Enter notes..."
},
contracts: {
title: "Contracts",
subtitle: "Manage your contracts and agreements",
newContract: "New Contract",
editContract: "Edit Contract",
deleteContract: "Delete Contract",
contractNumber: "Contract Number",
contractName: "Contract Name",
customerContractNumber: "Customer Contract Number",
customer: "Customer",
investor: "Investor",
dateSigned: "Date Signed",
finishDate: "Finish Date",
searchPlaceholder: "Search contracts by number, name, customer or investor...",
noContracts: "No contracts",
noContractsMessage: "Get started by creating your first contract.",
noMatchingContracts: "No matching contracts",
changeSearchCriteria: "Try changing your search criteria or filters.",
all: "All",
active: "Active",
withoutEndDate: "In Progress",
expired: "Expired",
createContract: "Create Contract",
enterContractNumber: "Enter contract number",
enterContractName: "Enter contract name",
enterCustomerContractNumber: "Enter customer contract number",
enterCustomerName: "Enter customer name",
enterInvestorName: "Enter investor name",
signedOn: "Signed:",
finishOn: "Finish:",
customerLabel: "Customer:",
investorLabel: "Investor:"
},
tasks: {
title: "Tasks",
task: "task",
tasks: "tasks",
subtitle: "Manage project tasks",
newTask: "New Task",
editTask: "Edit Task",
deleteTask: "Delete Task",
taskName: "Task Name",
description: "Description",
priority: "Priority",
assignedTo: "Assigned To",
dueDate: "Due Date",
status: "Status",
project: "Project",
template: "Template",
searchPlaceholder: "Search tasks...",
noTasks: "No tasks",
noTasksMessage: "No tasks assigned to this project yet.",
addTaskMessage: "Add a task above to get started.",
filterBy: "Filter by",
sortBy: "Sort by",
allTasks: "All Tasks",
myTasks: "My Tasks",
overdue: "Overdue",
dueSoon: "Due Soon",
high: "High",
normal: "Normal",
medium: "Medium",
low: "Low",
dateCreated: "Date Created",
dateModified: "Date Modified",
daysLeft: "days left",
daysOverdue: "days overdue",
dueToday: "Due Today",
startTask: "Start Task",
completeTask: "Complete Task",
comments: "Comments",
addComment: "Add Comment",
noComments: "No comments",
maxWait: "Max Wait",
dateStarted: "Date Started",
actions: "Actions",
urgent: "Urgent",
updateTask: "Update Task",
notStarted: "Not started",
days: "days"
},
taskTemplates: {
title: "Task Templates",
subtitle: "Manage task templates",
newTemplate: "New Template",
editTemplate: "Edit Template",
deleteTemplate: "Delete Template",
templateName: "Template Name",
templateDescription: "Template Description",
defaultPriority: "Default Priority",
estimatedDuration: "Estimated Duration",
category: "Category",
searchPlaceholder: "Search templates...",
noTemplates: "No templates",
noTemplatesMessage: "Get started by creating your first task template.",
useTemplate: "Use Template",
duplicateTemplate: "Duplicate Template"
},
forms: {
validation: {
required: "This field is required",
email: "Please enter a valid email address",
minLength: "Minimum length: {min} characters",
maxLength: "Maximum length: {max} characters",
invalidDate: "Invalid date",
invalidCoordinates: "Invalid coordinates"
},
placeholders: {
enterText: "Enter text...",
selectDate: "Select date",
selectOption: "Select option",
searchResults: "Search results..."
}
},
sorting: {
sortBy: "Sort by",
orderBy: "Order by",
ascending: "Ascending",
descending: "Descending",
name: "Name",
date: "Date",
status: "Status",
priority: "Priority",
dateCreated: "Date Created",
dateModified: "Date Modified",
startDate: "Start Date",
finishDate: "Finish Date"
},
dates: {
today: "Today",
yesterday: "Yesterday",
tomorrow: "Tomorrow",
daysAgo: "{count} days ago",
inDays: "in {count} days",
invalidDate: "Invalid date",
selectDate: "Select date"
},
mapLayers: {
polishOrthophoto: "🛰️ Polish Orthophoto",
polishCadastral: "📋 Polish Cadastral Data",
polishSpatialPlanning: "🏗️ Polish Spatial Planning",
lpPortalRoads: "🛣️ LP-Portal Roads",
lpPortalStreetNames: "🏷️ LP-Portal Street Names",
lpPortalParcels: "📐 LP-Portal Parcels",
lpPortalSurveyMarkers: "📍 LP-Portal Survey Markers",
openStreetMap: "🗺️ OpenStreetMap",
googleSatellite: "🛰️ Google Satellite",
googleRoads: "🛣️ Google Roads",
layerControl: "Layer Control",
baseLayers: "Base Layers",
overlayLayers: "Overlay Layers",
opacity: "Opacity"
},
auth: {
signIn: "Sign In",
signOut: "Sign Out",
email: "Email",
password: "Password",
rememberMe: "Remember Me",
forgotPassword: "Forgot Password?",
signInWith: "Sign in with",
signUp: "Sign Up",
createAccount: "Create Account",
alreadyHaveAccount: "Already have an account?",
dontHaveAccount: "Don't have an account?",
signInError: "Sign in error",
signOutError: "Sign out error",
emailRequired: "Email is required",
passwordRequired: "Password is required",
invalidCredentials: "Invalid credentials"
},
userRoles: {
admin: "Administrator",
user: "User",
manager: "Manager",
viewer: "Viewer"
},
errors: {
generic: "An error occurred. Please try again.",
network: "Network connection error",
unauthorized: "Unauthorized",
forbidden: "Forbidden",
notFound: "Not found",
validation: "Validation error",
server: "Server error",
timeout: "Request timeout",
unknown: "Unknown error"
},
success: {
saved: "Saved successfully",
created: "Created successfully",
updated: "Updated successfully",
deleted: "Deleted successfully",
sent: "Sent successfully",
completed: "Completed successfully"
},
admin: {
title: "Administration",
userManagement: "User Management",
auditLogs: "Audit Logs",
systemSettings: "System Settings",
users: "Users",
newUser: "New User",
editUser: "Edit User",
deleteUser: "Delete User",
userName: "User Name",
userEmail: "User Email",
userRole: "User Role",
active: "Active",
inactive: "Inactive",
lastLogin: "Last Login",
createdAt: "Created At",
noUsers: "No users",
system: "System"
}
}
};
// Translation provider component
export function TranslationProvider({ children, initialLanguage = 'pl' }) {
const [language, setLanguage] = useState(initialLanguage);
// Load language from localStorage on mount
useEffect(() => {
const savedLanguage = localStorage.getItem('app-language');
if (savedLanguage && translations[savedLanguage]) {
setLanguage(savedLanguage);
}
}, []);
// Save language to localStorage when changed
useEffect(() => {
localStorage.setItem('app-language', language);
}, [language]);
const changeLanguage = (newLanguage) => {
if (translations[newLanguage]) {
setLanguage(newLanguage);
}
};
const t = (key, replacements = {}) => {
const keys = key.split('.');
let value = translations[language];
for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
value = value[k];
} else {
// Fallback to English if key not found in current language
value = translations.en;
for (const fallbackKey of keys) {
if (value && typeof value === 'object' && fallbackKey in value) {
value = value[fallbackKey];
} else {
console.warn(`Translation key not found: ${key} (language: ${language})`);
return key; // Return the key itself as fallback
}
}
break;
}
}
if (typeof value !== 'string') {
console.warn(`Translation value is not a string: ${key} (language: ${language})`);
return key;
}
// Replace placeholders like {count}, {min}, {max}, etc.
let result = value;
Object.keys(replacements).forEach(placeholder => {
result = result.replace(new RegExp(`{${placeholder}}`, 'g'), replacements[placeholder]);
});
return result;
};
return (
<TranslationContext.Provider value={{
t,
language,
changeLanguage,
availableLanguages: Object.keys(translations)
}}>
{children}
</TranslationContext.Provider>
);
}
// Hook to use translations
export function useTranslation() {
const context = useContext(TranslationContext);
if (!context) {
throw new Error('useTranslation must be used within a TranslationProvider');
}
return context;
}
// Export translations for direct access if needed
export { translations };