glam/frontend/src/contexts/LanguageContext.tsx
kempersc 3a6ead8fde feat: Add legal form filtering rule for CustodianName
- Introduced LEGAL-FORM-FILTER rule to standardize CustodianName by removing legal form designations.
- Documented rationale, examples, and implementation guidelines for the filtering process.

docs: Create README for value standardization rules

- Established a comprehensive README outlining various value standardization rules applicable to Heritage Custodian classes.
- Categorized rules into Name Standardization, Geographic Standardization, Web Observation, and Schema Evolution.

feat: Implement transliteration standards for non-Latin scripts

- Added TRANSLIT-ISO rule to ensure GHCID abbreviations are generated from emic names using ISO standards for transliteration.
- Included detailed guidelines for various scripts and languages, along with implementation examples.

feat: Define XPath provenance rules for web observations

- Created XPATH-PROVENANCE rule mandating XPath pointers for claims extracted from web sources.
- Established a workflow for archiving websites and verifying claims against archived HTML.

chore: Update records lifecycle diagram

- Generated a new Mermaid diagram illustrating the records lifecycle for heritage custodians.
- Included phases for active records, inactive archives, and processed heritage collections with key relationships and classifications.
2025-12-09 16:58:41 +01:00

176 lines
6.5 KiB
TypeScript

/**
* 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<LanguageContextType | undefined>(undefined);
interface LanguageProviderProps {
children: ReactNode;
}
export function LanguageProvider({ children }: LanguageProviderProps) {
// Default to Dutch as primary language
const [language, setLanguage] = useState<Language>(() => {
// 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 (
<LanguageContext.Provider value={{ language, setLanguage: handleSetLanguage, toggleLanguage, t }}>
{children}
</LanguageContext.Provider>
);
}
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<string, { nl: string; en: string }>;
const item = section[subKey];
if (!item) return subKey;
return language === 'en' ? item.en : item.nl;
}