navidrome/ui/src/App.jsx
Deluan Quintão 9cd2cd0a8b
fix(ui): load ND_DEFAULTLANGUAGE on app startup (#4000)
* fix: load ND_DEFAULTLANGUAGE on app startup

Added  in  to apply  on initial mount, ensuring the locale is set even when the login page is skipped by reverse-proxy authentication. Removed the redundant language-init effect from . Fixes #3605.

* style(ui): format App.jsx with Prettier

Ran Prettier on ui/src/App.jsx to satisfy code style checks after adding default-language useEffect.

* fix(ui): move default language initialization to Admin component

Signed-off-by: Deluan <deluan@navidrome.org>

* fix(ui): streamline locale setting in App component

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
2026-06-09 19:27:15 -04:00

207 lines
5.7 KiB
JavaScript

import ReactGA from 'react-ga'
import { Provider } from 'react-redux'
import { createHashHistory } from 'history'
import {
Admin as RAAdmin,
Resource,
useSetLocale,
useRefresh,
} from 'react-admin'
import { HotKeys } from 'react-hotkeys'
import dataProvider from './dataProvider'
import authProvider from './authProvider'
import { Layout, Login, Logout } from './layout'
import transcoding from './transcoding'
import player from './player'
import user from './user'
import song from './song'
import album from './album'
import artist from './artist'
import playlist from './playlist'
import radio from './radio'
import share from './share'
import library from './library'
import plugin from './plugin'
import { Player } from './audioplayer'
import customRoutes from './routes'
import {
libraryReducer,
themeReducer,
addToPlaylistDialogReducer,
expandInfoDialogReducer,
listenBrainzTokenDialogReducer,
saveQueueDialogReducer,
playerReducer,
albumViewReducer,
activityReducer,
settingsReducer,
replayGainReducer,
downloadMenuDialogReducer,
shareDialogReducer,
transcodingReducer,
} from './reducers'
import createAdminStore from './store/createAdminStore'
import { i18nProvider, retrieveTranslation } from './i18n'
import config, { shareInfo } from './config'
import { keyMap } from './hotkeys'
import useChangeThemeColor from './useChangeThemeColor'
import SharePlayer from './share/SharePlayer'
import { HTML5Backend } from 'react-dnd-html5-backend'
import { DndProvider } from 'react-dnd'
import missing from './missing/index.js'
import { useEffect } from 'react'
const history = createHashHistory()
if (config.gaTrackingId) {
ReactGA.initialize(config.gaTrackingId)
history.listen((location) => {
ReactGA.pageview(location.pathname)
})
ReactGA.pageview(window.location.pathname)
}
const adminStore = createAdminStore({
authProvider,
dataProvider,
history,
customReducers: {
library: libraryReducer,
player: playerReducer,
albumView: albumViewReducer,
theme: themeReducer,
addToPlaylistDialog: addToPlaylistDialogReducer,
downloadMenuDialog: downloadMenuDialogReducer,
expandInfoDialog: expandInfoDialogReducer,
listenBrainzTokenDialog: listenBrainzTokenDialogReducer,
saveQueueDialog: saveQueueDialogReducer,
shareDialog: shareDialogReducer,
activity: activityReducer,
settings: settingsReducer,
replayGain: replayGainReducer,
transcoding: transcodingReducer,
},
})
const App = () => (
<Provider store={adminStore}>
<Admin />
</Provider>
)
const Admin = (props) => {
const setLocale = useSetLocale()
const refresh = useRefresh()
useEffect(() => {
if (config.defaultLanguage !== '' && !localStorage.getItem('locale')) {
retrieveTranslation(config.defaultLanguage)
.then(() => setLocale(config.defaultLanguage))
.then(() => {
localStorage.setItem('locale', config.defaultLanguage)
refresh(true)
})
.catch((e) => {
// eslint-disable-next-line no-console
console.error(
'Cannot load language "' + config.defaultLanguage + '": ' + e,
)
})
}
}, [setLocale, refresh])
useChangeThemeColor()
/* eslint-disable react/jsx-key */
return (
<RAAdmin
disableTelemetry
dataProvider={dataProvider}
authProvider={authProvider}
i18nProvider={i18nProvider}
customRoutes={customRoutes}
history={history}
layout={Layout}
loginPage={Login}
logoutButton={Logout}
{...props}
>
{(permissions) => [
<Resource name="album" {...album} options={{ subMenu: 'albumList' }} />,
<Resource name="artist" {...artist} />,
<Resource name="song" {...song} />,
<Resource
name="radio"
{...(permissions === 'admin' ? radio.admin : radio.all)}
/>,
config.enableSharing && <Resource name="share" {...share} />,
<Resource
name="playlist"
{...playlist}
options={{ subMenu: 'playlist' }}
/>,
<Resource name="user" {...user} options={{ subMenu: 'settings' }} />,
<Resource
name="player"
{...player}
options={{ subMenu: 'settings' }}
/>,
permissions === 'admin' ? (
<Resource
name="transcoding"
{...transcoding}
options={{ subMenu: 'settings' }}
/>
) : (
<Resource name="transcoding" />
),
permissions === 'admin' ? (
<Resource
name="library"
{...library}
options={{ subMenu: 'settings' }}
/>
) : null,
permissions === 'admin' ? (
<Resource
name="missing"
{...missing}
options={{ subMenu: 'settings' }}
/>
) : null,
permissions === 'admin' && config.pluginsEnabled ? (
<Resource
name="plugin"
{...plugin}
options={{ subMenu: 'settings' }}
/>
) : null,
<Resource name="translation" />,
<Resource name="genre" />,
<Resource name="tag" />,
<Resource name="playlistTrack" />,
<Resource name="keepalive" />,
<Resource name="insights" />,
<Resource name="config" />,
<Player />,
]}
</RAAdmin>
)
/* eslint-enable react/jsx-key */
}
const AppWithHotkeys = () => {
let language = localStorage.getItem('locale') || 'en'
document.documentElement.lang = language
if (config.enableSharing && shareInfo) {
return <SharePlayer />
}
return (
<HotKeys keyMap={keyMap}>
<DndProvider backend={HTML5Backend}>
<App />
</DndProvider>
</HotKeys>
)
}
export default AppWithHotkeys