mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
feat(login): add language picker on the login page (#328)
The language selector previously lived only in Settings, which is behind login. New users on a non-English system had no way to switch to their language before connecting to a server. Add a compact CustomSelect in the top-right of the login card. Reuses the existing settings.languageXx labels and i18n.changeLanguage flow (persists to localStorage as psysonic_language). English remains the default for first launches — already the case in i18n.ts:12. Styled to be visually quieter than the Settings variant (transparent background, smaller font) so it doesn't pull focus from the logo and form. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
d67db230ad
commit
9fe81ee6f6
+20
-1
@@ -1,9 +1,11 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { Wifi, WifiOff, Eye, EyeOff, Server } from 'lucide-react';
|
import { Wifi, WifiOff, Eye, EyeOff, Server, Globe } from 'lucide-react';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { pingWithCredentials, scheduleInstantMixProbeForServer } from '../api/subsonic';
|
import { pingWithCredentials, scheduleInstantMixProbeForServer } from '../api/subsonic';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import i18n from '../i18n';
|
||||||
|
import CustomSelect from '../components/CustomSelect';
|
||||||
import {
|
import {
|
||||||
decodeServerMagicString,
|
decodeServerMagicString,
|
||||||
DECODED_PASSWORD_VISUAL_MASK,
|
DECODED_PASSWORD_VISUAL_MASK,
|
||||||
@@ -168,6 +170,23 @@ export default function Login() {
|
|||||||
<div className="login-page">
|
<div className="login-page">
|
||||||
<div className="login-bg" aria-hidden="true" />
|
<div className="login-bg" aria-hidden="true" />
|
||||||
<div className="login-card animate-fade-in">
|
<div className="login-card animate-fade-in">
|
||||||
|
<div className="login-lang-picker" aria-label={t('settings.language')}>
|
||||||
|
<Globe size={14} aria-hidden="true" />
|
||||||
|
<CustomSelect
|
||||||
|
value={i18n.language}
|
||||||
|
onChange={v => i18n.changeLanguage(v)}
|
||||||
|
options={[
|
||||||
|
{ value: 'en', label: t('settings.languageEn') },
|
||||||
|
{ value: 'de', label: t('settings.languageDe') },
|
||||||
|
{ value: 'es', label: t('settings.languageEs') },
|
||||||
|
{ value: 'fr', label: t('settings.languageFr') },
|
||||||
|
{ value: 'nl', label: t('settings.languageNl') },
|
||||||
|
{ value: 'nb', label: t('settings.languageNb') },
|
||||||
|
{ value: 'ru', label: t('settings.languageRu') },
|
||||||
|
{ value: 'zh', label: t('settings.languageZh') },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="login-logo">
|
<div className="login-logo">
|
||||||
<PsysonicLogo />
|
<PsysonicLogo />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4134,6 +4134,33 @@
|
|||||||
margin-bottom: var(--space-5);
|
margin-bottom: var(--space-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-lang-picker {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--space-3);
|
||||||
|
right: var(--space-3);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-1);
|
||||||
|
color: var(--text-muted);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-lang-picker > button {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
color: var(--text-muted);
|
||||||
|
width: auto;
|
||||||
|
min-width: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-lang-picker > button:hover {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
border-color: var(--border-subtle);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
.login-title {
|
.login-title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
|
|||||||
Reference in New Issue
Block a user