11// SPDX-FileCopyrightText: 2024 German Aerospace Center (DLR)
22// SPDX-License-Identifier: Apache-2.0
33
4- import React , { useState } from 'react' ;
4+ import React , { useState , useMemo , useCallback } from 'react' ;
55import { Box , Typography , Divider , TextField , IconButton , InputAdornment , CircularProgress } from '@mui/material' ;
66import { useSemanticSearch } from 'context/SemanticSearchContext' ;
77import QuerySuggestions from './QuerySuggestions' ;
@@ -12,20 +12,46 @@ import {setSemanticSearchQuery} from 'store/SemanticSearchSlice';
1212import SearchResultsList from './SearchResultsList' ;
1313import ArticleDialog from './ArticleDialog' ;
1414import { SearchResult } from 'types/semanticSearch' ;
15+ import { Localization } from 'types/localization' ;
1516
1617/**
1718 * Main container for the Semantic Search feature.
1819 * It will manage the search state and orchestrate the child components.
1920 */
20- export default function SemanticSearchContainer ( ) : JSX . Element {
21+ export default function SemanticSearchContainer ( { localization } : { localization ?: Localization } ) : JSX . Element {
2122 const { searchResults, isLoading, performSearch, clearSearch} = useSemanticSearch ( ) ;
2223 const dispatch = useAppDispatch ( ) ;
2324 const query = useAppSelector ( ( state ) => state . semanticSearch . searchQuery ) ;
2425 const searchStatus = useAppSelector ( ( state ) => state . semanticSearch . searchStatus ) ;
25- const { t} = useTranslation ( 'global' ) ;
26+ const { t : defaultT , i18n } = useTranslation ( ) ;
2627 const [ selectedArticle , setSelectedArticle ] = useState < SearchResult | null > ( null ) ;
2728
28- const suggestions = [ t ( 'semanticSearch.suggestions.scenarioForecasting' ) , t ( 'semanticSearch.suggestions.purpose' ) ] ;
29+ const memoizedLocalization = useMemo (
30+ ( ) =>
31+ localization ?? {
32+ formatNumber : ( v : number ) => v . toLocaleString ( i18n . language ) ,
33+ customLang : 'global' ,
34+ overrides : { } ,
35+ } ,
36+ [ localization , i18n . language ]
37+ ) ;
38+
39+ const { t : customT } = useTranslation ( memoizedLocalization . customLang ) ;
40+
41+ const tOverride = useCallback (
42+ ( key : string , options ?: { [ key : string ] : string | number } ) =>
43+ memoizedLocalization . overrides ?. [ key ]
44+ ? customT ( memoizedLocalization . overrides [ key ] , options )
45+ : defaultT ( key , options ) ,
46+ [ customT , defaultT , memoizedLocalization . overrides ]
47+ ) ;
48+
49+ const fmtNum = memoizedLocalization . formatNumber ?? ( ( v : number ) => v . toLocaleString ( i18n . language ) ) ;
50+
51+ const suggestions = [
52+ tOverride ( 'semanticSearch.suggestions.scenarioForecasting' ) ,
53+ tOverride ( 'semanticSearch.suggestions.purpose' ) ,
54+ ] ;
2955
3056 const handleSearch = ( ) => {
3157 // performSearch is now required and will not be undefined.
@@ -58,17 +84,17 @@ export default function SemanticSearchContainer(): JSX.Element {
5884 < Box mt = { 4 } >
5985 < Divider sx = { { mb : 3 } } />
6086 < Typography variant = 'h2' gutterBottom >
61- { t ( 'semanticSearch.title' ) }
87+ { tOverride ( 'semanticSearch.title' ) }
6288 </ Typography >
6389 < Typography variant = 'body1' paragraph sx = { { color : 'GrayText' } } >
64- { t ( 'semanticSearch.description' ) }
90+ { tOverride ( 'semanticSearch.description' ) }
6591 </ Typography >
6692
6793 < TextField
6894 fullWidth
6995 size = 'small'
7096 variant = 'outlined'
71- placeholder = { t ( 'semanticSearch.placeholder' ) }
97+ placeholder = { tOverride ( 'semanticSearch.placeholder' ) }
7298 value = { query }
7399 onChange = { ( e ) => dispatch ( setSemanticSearchQuery ( e . target . value ) ) }
74100 onKeyDown = { ( e ) => {
@@ -108,7 +134,11 @@ export default function SemanticSearchContainer(): JSX.Element {
108134 } }
109135 />
110136
111- < QuerySuggestions suggestions = { suggestions } onSuggestionClick = { handleSuggestionClick } />
137+ < QuerySuggestions
138+ suggestions = { suggestions }
139+ onSuggestionClick = { handleSuggestionClick }
140+ localization = { memoizedLocalization }
141+ />
112142
113143 < Box
114144 mt = { 3 }
@@ -120,11 +150,11 @@ export default function SemanticSearchContainer(): JSX.Element {
120150 >
121151 { searchResults . length > 0 && (
122152 < Typography variant = 'body2' sx = { { color : 'GrayText' , mb : 1.5 , px : 1 } } >
123- { t (
153+ { tOverride (
124154 searchResults . length === 1
125155 ? 'semanticSearch.results.resultsFound_one'
126156 : 'semanticSearch.results.resultsFound_other' ,
127- { count : searchResults . length }
157+ { count : fmtNum ( searchResults . length ) }
128158 ) } { ' ' }
129159 "
130160 < Box component = 'span' sx = { { fontStyle : 'italic' , color : 'primary.main' } } >
@@ -138,10 +168,14 @@ export default function SemanticSearchContainer(): JSX.Element {
138168 < CircularProgress />
139169 </ Box >
140170 ) : searchResults . length > 0 ? (
141- < SearchResultsList results = { searchResults } onResultClick = { handleResultClick } />
171+ < SearchResultsList
172+ results = { searchResults }
173+ onResultClick = { handleResultClick }
174+ localization = { memoizedLocalization }
175+ />
142176 ) : searchStatus === 'succeeded' ? (
143177 < Box display = 'flex' justifyContent = 'center' alignItems = 'center' sx = { { py : 4 } } >
144- < Typography sx = { { color : 'GrayText' } } > { t ( 'semanticSearch.results.noResults' ) } </ Typography >
178+ < Typography sx = { { color : 'GrayText' } } > { tOverride ( 'semanticSearch.results.noResults' ) } </ Typography >
145179 </ Box >
146180 ) : null }
147181 </ Box >
0 commit comments