/** * Language Context for bilingual support (NL/EN) * * Provides a global language state that can be used across all pages. * LinkML and ontology descriptions should remain in their original language. * * © 2025 Netwerk Digitaal Erfgoed & TextPast. All rights reserved. */ import { createContext, useContext, useState, useCallback, type ReactNode } from 'react'; export type Language = 'nl' | 'en'; interface LanguageContextType { language: Language; setLanguage: (lang: Language) => void; toggleLanguage: () => void; t: (nl: string, en?: string) => string; } const LanguageContext = createContext(undefined); interface LanguageProviderProps { children: ReactNode; } export function LanguageProvider({ children }: LanguageProviderProps) { // Default to Dutch as primary language const [language, setLanguage] = useState(() => { // Check localStorage for saved preference const saved = localStorage.getItem('glam-language'); return (saved === 'en' || saved === 'nl') ? saved : 'nl'; }); const handleSetLanguage = useCallback((lang: Language) => { setLanguage(lang); localStorage.setItem('glam-language', lang); }, []); const toggleLanguage = useCallback(() => { const newLang = language === 'nl' ? 'en' : 'nl'; handleSetLanguage(newLang); }, [language, handleSetLanguage]); // Translation helper - returns English text if available and language is EN, otherwise Dutch const t = useCallback((nl: string, en?: string): string => { return language === 'en' && en ? en : nl; }, [language]); return ( {children} ); } export function useLanguage() { const context = useContext(LanguageContext); if (context === undefined) { throw new Error('useLanguage must be used within a LanguageProvider'); } return context; } // Common translations for UI elements export const translations = { // Navigation nav: { home: { nl: 'Home', en: 'Home' }, visualize: { nl: 'Visualiseren', en: 'Visualize' }, database: { nl: 'Database', en: 'Database' }, query: { nl: 'Zoek', en: 'Query' }, linkml: { nl: 'LinkML', en: 'LinkML' }, ontology: { nl: 'Ontologie', en: 'Ontology' }, map: { nl: 'Kaart', en: 'Map' }, stats: { nl: 'Statistieken', en: 'Stats' }, overview: { nl: 'Overzicht', en: 'Overview' }, gesprek: { nl: 'Gesprek', en: 'Chat' }, settings: { nl: 'Instellingen', en: 'Settings' }, signOut: { nl: 'Uitloggen', en: 'Sign Out' }, }, // Common UI common: { loading: { nl: 'Laden...', en: 'Loading...' }, error: { nl: 'Fout', en: 'Error' }, search: { nl: 'Zoeken', en: 'Search' }, filter: { nl: 'Filteren', en: 'Filter' }, save: { nl: 'Opslaan', en: 'Save' }, cancel: { nl: 'Annuleren', en: 'Cancel' }, close: { nl: 'Sluiten', en: 'Close' }, back: { nl: 'Terug', en: 'Back' }, next: { nl: 'Volgende', en: 'Next' }, previous: { nl: 'Vorige', en: 'Previous' }, results: { nl: 'resultaten', en: 'results' }, noResults: { nl: 'Geen resultaten gevonden', en: 'No results found' }, total: { nl: 'Totaal', en: 'Total' }, hours: { nl: 'uren', en: 'hours' }, week: { nl: 'week', en: 'week' }, status: { nl: 'Status', en: 'Status' }, }, // Project Plan specific projectPlan: { title: { nl: 'Projectplan', en: 'Project Plan' }, timeline: { nl: 'Tijdlijn', en: 'Timeline' }, workPackages: { nl: 'Werkpakketten', en: 'Work Packages' }, ontologies: { nl: 'Ontologieën', en: 'Ontologies' }, outOfScope: { nl: 'Buiten scope', en: 'Out of Scope' }, totalHours: { nl: 'Totaal uren', en: 'Total Hours' }, deliverables: { nl: 'Deliverables', en: 'Deliverables' }, ontologyAlignments: { nl: 'Ontologie verbindingen', en: 'Ontology Alignments' }, hoursPerWP: { nl: 'Uren per werkpakket', en: 'Hours per Work Package' }, lastUpdated: { nl: 'Laatst bijgewerkt', en: 'Last updated' }, commissioner: { nl: 'Opdrachtgever', en: 'Commissioner' }, notIncluded: { nl: 'Niet inbegrepen in dit project', en: 'Not Included in This Project' }, futureScope: { nl: 'Toekomstig', en: 'Future' }, rationale: { nl: 'Reden', en: 'Rationale' }, ontologyNetwork: { nl: 'Ontologie Afstemming Netwerk', en: 'Ontology Alignment Network' }, networkDescription: { nl: 'Visualisatie van de verbindingen tussen de Bronhouder Ontologie en bestaande ontologieën.', en: 'Visualization of connections between the Heritage Custodian Ontology and existing ontologies.' }, extension: { nl: 'Uitbreiding', en: 'Extension' }, integration: { nl: 'Integratie', en: 'Integration' }, mapping: { nl: 'Mapping', en: 'Mapping' }, }, // Visualize page visualize: { title: { nl: 'Schema Visualisatie', en: 'Schema Visualization' }, selectDiagram: { nl: 'Selecteer diagram', en: 'Select diagram' }, zoom: { nl: 'Zoom', en: 'Zoom' }, reset: { nl: 'Reset', en: 'Reset' }, export: { nl: 'Exporteren', en: 'Export' }, }, // Database page database: { title: { nl: 'Database Verkenner', en: 'Database Explorer' }, tables: { nl: 'Tabellen', en: 'Tables' }, records: { nl: 'Records', en: 'Records' }, columns: { nl: 'Kolommen', en: 'Columns' }, }, // Map page map: { title: { nl: 'Erfgoedinstellingen Kaart', en: 'Heritage Institutions Map' }, institutions: { nl: 'Instellingen', en: 'Institutions' }, province: { nl: 'Provincie', en: 'Province' }, city: { nl: 'Plaats', en: 'City' }, type: { nl: 'Type', en: 'Type' }, }, // Stats page stats: { title: { nl: 'Statistieken', en: 'Statistics' }, byProvince: { nl: 'Per provincie', en: 'By Province' }, byType: { nl: 'Per type', en: 'By Type' }, overview: { nl: 'Overzicht', en: 'Overview' }, }, // Settings page settings: { title: { nl: 'Instellingen', en: 'Settings' }, language: { nl: 'Taal', en: 'Language' }, theme: { nl: 'Thema', en: 'Theme' }, darkMode: { nl: 'Donkere modus', en: 'Dark Mode' }, notifications: { nl: 'Notificaties', en: 'Notifications' }, }, } as const; // Helper to get translation from the translations object export function getTranslation( key: keyof typeof translations, subKey: string, language: Language ): string { const section = translations[key] as Record; const item = section[subKey]; if (!item) return subKey; return language === 'en' ? item.en : item.nl; }