feat: i18n
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Slider } from '@olly/modern-sk';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon } from '../common/Icon';
|
||||
import { ArtTile } from '../common/ArtTile';
|
||||
import { useAppDispatch, useAppSelector } from '../../hooks/useAppDispatch';
|
||||
@@ -17,6 +18,7 @@ import { formatDuration } from '../../lib/format';
|
||||
import { getCoverUrl } from '../../api/endpoints/streaming';
|
||||
|
||||
export function PersistentPlayer() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { seek, playNext, playPrev } = useAudioPlayer();
|
||||
const player = useAppSelector((s) => s.player);
|
||||
@@ -24,17 +26,15 @@ export function PersistentPlayer() {
|
||||
const currentEntry = queue.entries[queue.currentIndex];
|
||||
|
||||
if (!currentEntry && !player.currentTrackId) {
|
||||
return <div className="player empty">Nothing playing</div>;
|
||||
return <div className="player empty">{t('player.nothingPlaying')}</div>;
|
||||
}
|
||||
|
||||
const artUrl = getCoverUrl(currentEntry?.albumArtUrl);
|
||||
const seedLabel = currentEntry?.albumTitle ?? currentEntry?.title ?? '';
|
||||
// Streaming is the web default; local playback is a mobile-client concern.
|
||||
const onStream = true;
|
||||
|
||||
return (
|
||||
<div className="player">
|
||||
{/* now-playing identity */}
|
||||
<div
|
||||
className="pl-now"
|
||||
onClick={() => dispatch(toggleNowPlaying())}
|
||||
@@ -49,19 +49,18 @@ export function PersistentPlayer() {
|
||||
style={{ color: onStream ? 'var(--fg-3)' : 'var(--lime)' }}
|
||||
>
|
||||
<Icon name={onStream ? 'cloud' : 'check-circle'} fill={!onStream} />
|
||||
{onStream ? 'Streaming · 320 kbps' : 'Local · FLAC'}
|
||||
{onStream ? t('player.streaming') : t('player.local')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* transport + scrubber */}
|
||||
<div className="pl-center">
|
||||
<div className="pl-transport">
|
||||
<button
|
||||
type="button"
|
||||
className={`pl-tbtn${player.shuffle ? ' on' : ''}`}
|
||||
onClick={() => dispatch(toggleShuffle())}
|
||||
title="Shuffle"
|
||||
title={t('player.shuffle')}
|
||||
>
|
||||
<Icon name="shuffle" />
|
||||
</button>
|
||||
@@ -69,7 +68,7 @@ export function PersistentPlayer() {
|
||||
type="button"
|
||||
className="pl-tbtn"
|
||||
onClick={playPrev}
|
||||
title="Previous"
|
||||
title={t('player.previous')}
|
||||
>
|
||||
<Icon name="skip-back" fill />
|
||||
</button>
|
||||
@@ -79,7 +78,7 @@ export function PersistentPlayer() {
|
||||
onClick={() =>
|
||||
player.isPlaying ? dispatch(pause()) : dispatch(resume())
|
||||
}
|
||||
title={player.isPlaying ? 'Pause' : 'Play'}
|
||||
title={player.isPlaying ? t('player.pause') : t('player.play')}
|
||||
>
|
||||
<Icon name={player.isPlaying ? 'pause' : 'play'} fill />
|
||||
</button>
|
||||
@@ -87,7 +86,7 @@ export function PersistentPlayer() {
|
||||
type="button"
|
||||
className="pl-tbtn"
|
||||
onClick={playNext}
|
||||
title="Next"
|
||||
title={t('player.next')}
|
||||
>
|
||||
<Icon name="skip-forward" fill />
|
||||
</button>
|
||||
@@ -105,7 +104,7 @@ export function PersistentPlayer() {
|
||||
),
|
||||
)
|
||||
}
|
||||
title={`Repeat: ${player.repeat}`}
|
||||
title={t('player.repeat', { mode: player.repeat })}
|
||||
>
|
||||
<Icon name="repeat" />
|
||||
</button>
|
||||
@@ -121,7 +120,7 @@ export function PersistentPlayer() {
|
||||
step={1}
|
||||
value={[player.position]}
|
||||
onValueChange={([v]) => seek(v)}
|
||||
aria-label="Seek"
|
||||
aria-label={t('player.play')}
|
||||
/>
|
||||
<span className="pl-time">
|
||||
{formatDuration(player.duration * 1000)}
|
||||
@@ -129,13 +128,12 @@ export function PersistentPlayer() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* volume + queue */}
|
||||
<div className="pl-right">
|
||||
<button
|
||||
type="button"
|
||||
className="pl-tbtn"
|
||||
onClick={() => dispatch(toggleMute())}
|
||||
title={player.muted ? 'Unmute' : 'Mute'}
|
||||
title={player.muted ? t('player.unmute') : t('player.mute')}
|
||||
>
|
||||
<Icon name={player.muted ? 'speaker-x' : 'speaker-high'} />
|
||||
</button>
|
||||
@@ -154,7 +152,7 @@ export function PersistentPlayer() {
|
||||
type="button"
|
||||
className={`iconbtn sm${player.isQueueOpen ? ' on' : ''}`}
|
||||
onClick={() => dispatch(toggleQueue())}
|
||||
title="Play queue"
|
||||
title={t('player.queue')}
|
||||
>
|
||||
<Icon name="queue" />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user