1
- import React , { useEffect , useRef } from 'react'
2
- import { Outlet , useLocation , useParams } from 'react-router-dom'
1
+ import React , { createContext , useContext , useEffect , useRef , useState } from 'react'
2
+ import { Outlet , useLocation , useNavigate , useParams } from 'react-router-dom'
3
3
import { SnackbarProvider } from 'notistack'
4
4
import { initShibbolethPinger } from 'unfuck-spa-shibboleth-session'
5
5
import { ThemeProvider } from '@mui/material/styles'
@@ -18,7 +18,11 @@ import useCurrentUser from './hooks/useCurrentUser'
18
18
import { EmbeddedProvider , useIsEmbedded } from './contexts/EmbeddedContext'
19
19
import { Feedback } from './components/Feedback'
20
20
import { AnalyticsProvider } from './stores/analytics'
21
+ import { useTranslation } from 'react-i18next'
21
22
23
+ function useQuery ( ) {
24
+ return new URLSearchParams ( useLocation ( ) . search ) ;
25
+ }
22
26
const hasAccess = ( user : User | null | undefined , courseId ?: string ) => {
23
27
if ( ! user ) return false
24
28
if ( user . isAdmin ) return true
@@ -70,18 +74,67 @@ const AdminLoggedInAsBanner = () => {
70
74
/>
71
75
)
72
76
}
77
+ const LanguageContext = createContext ( { } ) ;
78
+
79
+ export function LanguageProvider ( { children } ) {
80
+ const location = useLocation ( ) ;
81
+ const navigate = useNavigate ( ) ;
82
+ const [ lang , setLangState ] = useState ( 'fi' ) ; // default
83
+
84
+ // Sync context state to URL
85
+ const setLang = ( newLang ) => {
86
+ setLangState ( newLang ) ;
87
+ const searchParams = new URLSearchParams ( location . search ) ;
88
+ searchParams . set ( 'lang' , newLang ) ;
89
+ navigate ( `${ location . pathname } ?${ searchParams . toString ( ) } ` , { replace : true } ) ;
90
+ } ;
91
+
92
+ // Read URL param on initial load
93
+ useEffect ( ( ) => {
94
+ const searchParams = new URLSearchParams ( location . search ) ;
95
+ const urlLang = searchParams . get ( 'lang' ) ;
96
+ if ( urlLang ) {
97
+ setLangState ( urlLang ) ;
98
+ }
99
+ } , [ location . search ] ) ;
73
100
101
+ return (
102
+ < LanguageContext . Provider value = { { lang, setLang } } >
103
+ { children }
104
+ </ LanguageContext . Provider >
105
+ ) ;
106
+ }
107
+
108
+ export function useLanguage ( ) {
109
+ return useContext ( LanguageContext ) ;
110
+ }
74
111
const App = ( ) => {
75
112
const theme = useTheme ( )
76
113
const { courseId } = useParams ( )
77
114
const location = useLocation ( )
78
-
115
+ const query = useQuery ( )
116
+ const langParam = query . get ( 'lang' )
117
+ const { t, i18n } = useTranslation ( )
118
+ const { language } = i18n
119
+ const languages = [ 'fi' , 'sv' , 'en' ]
79
120
const { user, isLoading } = useCurrentUser ( )
80
-
121
+
81
122
useEffect ( ( ) => {
82
123
initShibbolethPinger ( )
83
124
} , [ ] )
125
+ useEffect ( ( ) => {
126
+ if ( ! langParam && user && user . language && languages . includes ( user . language ) ) {
127
+ i18n . changeLanguage ( user . language )
128
+
129
+ }
130
+ } , [ user , i18n ] )
131
+ useEffect ( ( ) => {
132
+ if ( langParam && languages . includes ( langParam ) ) {
84
133
134
+ i18n . changeLanguage ( langParam )
135
+ }
136
+
137
+ } , [ langParam ] )
85
138
const onNoAccessPage = location . pathname . includes ( '/noaccess' )
86
139
87
140
if ( isLoading && ! onNoAccessPage ) return null
@@ -94,6 +147,7 @@ const App = () => {
94
147
if ( ! user && ! onNoAccessPage ) return null
95
148
96
149
return (
150
+ < LanguageProvider >
97
151
< ThemeProvider theme = { theme } >
98
152
< CssBaseline />
99
153
< LocalizationProvider dateAdapter = { AdapterDateFns } adapterLocale = { fi } >
@@ -106,6 +160,7 @@ const App = () => {
106
160
</ SnackbarProvider >
107
161
</ LocalizationProvider >
108
162
</ ThemeProvider >
163
+ </ LanguageProvider >
109
164
)
110
165
}
111
166
0 commit comments