mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 14:55:43 +00:00
f9df918c72
* feat(themes): add semantic tokens for the theme-store contract (B0 P1) Additive: define --highlight, --accent-2, --bg-deep, --bg-elevated and --text-on-accent on the :root base as --ctp-* mappings. They resolve per-theme automatically and nothing consumes them yet (zero behaviour change) — groundwork for replacing direct --ctp-* use in components. * refactor(themes): components consume semantic tokens, not --ctp-* (B0 P2) Replace every direct --ctp-* reference in component/layout/track CSS and TSX inline styles with the readable semantic token (--bg-app, --accent, --highlight, --text-on-accent, …). --ctp-* now survives only as the Catppuccin palette layer the base maps from, and as the deliberate categorical rainbow in Composers/Genres/artistsHelpers (left untouched). This is the readable contract surface for the community theme store. Divergences (theme set a semantic var != its --ctp- source) are corrections — the element now uses the theme's real semantic colour. * feat(themes): add player-bar title/artist color tokens New optional --player-title / --player-artist, defaulting to --text-primary / --text-secondary so nothing changes unless a theme overrides them. Lets a theme give the now-playing readout its own colour as a plain token. * refactor(themes): token-only theme library (flatten, whitelist, one file per theme) Turn every built-in theme into a single self-contained [data-theme] var block of semantic whitelist tokens (plus the internal --ctp-* palette layer): - Flatten all themes: drop structural override rules, @keyframes, and global-token overrides (radius / shadow-elevation / transition / spacing / font / focus-ring). Signature player-bar readout colours are preserved via the new --player-title / --player-artist tokens. - Normalize the var blocks to the semantic whitelist: drop the alternate token vocabulary (nav-active / scrollbar / bg-input / success / border-default / ...); rename --success -> --positive and --border-default -> --border where no whitelist equivalent was set. Migrate the few components that read those tokens to the whitelist equivalents. - Split multi-theme files so each theme ships as its own file, making the built-in set 1:1 with the per-theme store packaging. Kept as-is (built-in, not flattened): the two colour-blind-safe accessibility themes, plus the two curated core skins. * chore(themes): remove seven themes retired after the token refactor These themes leaned on heavy structural overrides and were dropped rather than flattened. Full removal each: the CSS file(s), the index.css import, the Theme type union, and the ThemePicker entry. * feat(themes): granular tokens — track lists Wire track rows to per-region tokens: row hover (--row-hover), the now-playing row + indicator (--row-playing-bg / --row-playing-text), track title/artist/ number/duration text, column-header text, row dividers, and the resize-handle active colour. Covers the desktop tracklist, the shared song-row (Tracks hub / search), and the mobile tracklist. Drop a baked border fallback. Visual no-op. * feat(themes): granular tokens — cards Wire album and artist cards to per-region tokens (--card-hover-border, --card-title, --card-subtitle, --card-placeholder-bg). Visual no-op. * fix(themes): drop undefined/baked colour aliases Replace the undefined --bg-surface (resolved to nothing — broken placeholder backgrounds and filter input) with --card-placeholder-bg / --input-bg, and the baked-hex aliases --color-error / --color-warning with --danger / --warning so themes can actually recolour them. * feat(themes): Spectrum demo theme + trim unused cascade tokens Add a loud built-in demo theme that gives each region its own hue (sidebar green, player pink, lists cyan, cards gold, menus red, controls blue) so the per-region granularity is obvious when you switch to it. Drop two unused cascade tokens (--sidebar-text-active, --row-active-bg) and the unused on-media block (those media surfaces stay static by design). * style(themes): make Spectrum demo brutally loud Full-saturation neon per region (toxic green sidebar, magenta player, cyan lists, acid-yellow cards, blood-red menus, electric-blue controls, purple scrollbar) so the per-region separation is unmistakable. The earlier soft tints were too subtle to read. * feat(themes): name the granular demo theme Braindead * feat(themes): granular per-region tokens — cascade layer + sidebar Add an optional per-region token layer (semantic-cascade.css) so a theme can recolour individual regions — sidebar hover, player controls, list rows, menus, inputs, on-media surfaces — independently of the global tokens. Every token defaults to its base token (or a media-safe literal), so this is a visual no-op until a theme overrides one; it only adds control points. Wire the sidebar region as the first consumer and drop the baked grey fallbacks (--bg-tertiary, etc.) that no theme could reach. * feat(themes): granular tokens — controls, menus, scrollbar Wire inputs, buttons, sliders, the custom-select, context menus, submenus and modals to per-region tokens, and tokenise the scrollbar. Complete B0 by dropping the last direct --ctp-* references in the input/button/progress/ scrollbar utility CSS. Fix three undefined-token bugs that fell back to nothing (so no theme could reach them): --surface-2 (context-menu hover had no highlight), --bg-surface (submenu create-input had no background), and the baked grey fallbacks in the custom-select. Every other new token defaults to today's value — a visual no-op that only adds override points. * feat(themes): granular tokens — player bar Wire the desktop player bar's transport controls, time toggle, and overflow menu to per-region tokens (--player-control, --player-time-toggle-*, etc.). Fix the undefined --surface-hover/--surface-active grey fallbacks on the time toggle. Visual no-op; defaults match today's values. * chore(themes): remove empty theme stub files Six theme CSS files were reduced to comment-only stubs by the flatten sweep but their files and @import lines remained. Five are empty structural companions of now-flattened themes (morpheus, p-dvd, aero-glass, luna-teal) and two are orphans of cut themes (order-of-the-phoenix, pandora). Removed the files and their imports. * feat(themes): runtime injection foundation for the theme store Plumbing for installed community themes ahead of the in-app store UI, nothing user-visible yet: - installedThemesStore: persisted (localStorage) record of installed community themes incl. their CSS text, so an active community theme is available synchronously at startup (no flash, fully offline). - themeInjection: reconcile <head> <style data-installed-theme> elements with the store; lightweight defense-in-depth sanitize on top of CI. - themeRegistry: jsDelivr registry client with a 12h localStorage cache and stale-on-error fallback. - App: inject installed themes before applying data-theme, in both webviews. - themeStore: widen the Theme type to accept dynamic installed ids. * feat(themes): dedicated Themes settings tab Move theme selection and the day/night scheduler out of Appearance into a new dedicated Themes tab — the future home of the community Theme Store. Appearance keeps grid columns, visual options, UI scale, font and seekbar. - ThemesTab: theme picker + scheduler (relocated verbatim). - AppearanceTab: drop the two relocated sections + now-unused imports. - Register the tab in settingsTabs (Tab union, resolveTab, search index) and Settings (tab bar, render, label map). - i18n: settings.tabThemes in all 9 locales. * feat(themes): community Theme Store browse + install Add the Theme Store section to the Themes tab: - Fetch the jsDelivr registry (12h cache, stale-on-error fallback). - Search by name/author/description + filter by light/dark + refresh. - Per-row CDN thumbnail, name, author, description and actions: Install / Apply / Update / Uninstall. Installing fetches the CSS, persists it (localStorage) and the runtime injection applies it; uninstalling the active theme falls back to the matching core. - Rating slot left reserved (deferred). - i18n: themeStore* keys in all 9 locales. * feat(themes): slim bundle to fixed cores + flat Themes tab Remove the 86 store palettes (incl. braindead) from the app bundle — the CSS files, their index.css imports, the Theme union and the picker data — leaving only the six fixed cores (Catppuccin Mocha/Latte, Kanagawa Wave, Stark HUD, Vision Dark/Navy). Everything else installs from the store. Themes tab is rebuilt flat (no collapsible accordions): - "Your Themes": one card grid of the fixed cores + installed community themes; click to apply, uninstall on community ones (active theme falls back to the matching core). Catppuccin prefix on Mocha/Latte; a CVD-safe pill on the colour-blind-safe Vision themes. - Scheduler day/night options include installed themes. - Theme Store: alphabetical order, thumbnail lightbox, and a submit hint above the search linking to the themes repository. - Nav order: Servers, Library, Audio, Themes, Appearance, Lyrics, … - ThemePicker accordion removed; fixed-theme data moved to fixedThemes.ts. - i18n for all new strings across 9 locales. * feat(themes): reset removed-from-bundle themes to a bundled fallback After slimming the bundle to the six fixed core themes, a profile upgraded from an older build may have an active or scheduler theme that is now store-only and not installed — it has no [data-theme] block and would render as unstyled :root. Reset any theme/themeDay/themeNight that is neither bundled nor installed to a bundled fallback: Mocha for the main + night slots, Latte for the day slot. Runs synchronously in runPreReactBootstrap, rewriting the persisted selection in localStorage before React mounts (no flash; Zustand rehydrates after first paint). No auto-install and no network — the fallback is always a bundled theme, so it works offline. * feat(themes): floating back-to-top button on the Themes tab The Themes tab can get long (theme grid + scheduler + full store list), so add a floating back-to-top affordance that appears once the page is scrolled and smooth-scrolls to the top. It is portalled into the route host and positioned absolute against it — the main scroll viewport sets contain: paint, which would otherwise make position: fixed resolve against the scrolling box and drift with the content. Reusable component (scroll viewport id + threshold props); i18n common.backToTop added in all nine locales. * feat(themes): accessibility + state polish for the Theme Store - Reuse the shared CoverLightbox for the thumbnail preview instead of a second inline dialog — gains a visible close button and a focus-managed, portalled dialog, and drops duplicated markup. - Theme cards expose aria-pressed so assistive tech announces the selected theme, not just the visual check. - Transient store messages get live-region roles (loading/empty/install failure = status, fetch error = alert). - Thumbnails degrade gracefully when offline/missing (hide the broken-image glyph; the thumbnail button no longer stretches with the row, so its background can't show as letterbox bars). * feat(themes): larger store-row thumbnails (120x75 -> 200x125) The list previews were too small to make a theme out; bump the display size (same 1.6 aspect). Thumbnails are now served at 720x450, so the larger display stays crisp. * fix(themes): bust thumbnail cache on registry change jsDelivr serves theme thumbnails with a 7-day max-age, so when a thumbnail is updated the webview keeps showing its cached old image (the path is unchanged). Append the registry's generatedAt as a cache-busting query to the thumbnail URLs (list + lightbox); it changes on every themes push, so a registry refresh makes the webview re-fetch and reflect the current CDN image instead of a stale one. * docs(themes): changelog + credits for the Theme Store Add the 1.48.0 "Themes — community Theme Store" changelog entry (PR #1009) and the matching line in the Psychotoxical credits. * fix(themes): address PR review (uninstall hygiene, validation, polish) Uninstall/scheduler & validation: - uninstallTheme() repairs every selection slot (active + day + night), not just the manual one, and is shared by both uninstall buttons (dedup). - Validate theme CSS at install time and skip persisting CSS that won't inject (no more "installed/active but renders nothing" with no feedback). - Harden the runtime validator: exactly one rule, scoped exactly to the theme's [data-theme='<id>'] selector (no unscoped/foreign selectors), no at-rules, url() only data:, no expression()/javascript:, size-capped. Tokens & polish: - Fix three dangling undefined tokens (--surface-2 x2, --bg-surface). - Finish the warning/success token sweep (--warning / --positive, themeable). - Apply the active theme synchronously before React mounts (no first-frame flash) and inject installed themes up front. - One-time, dismissible notice when the slim-bundle migration reset a theme. - Update badge uses semver, not string inequality. - Offline/stale indicator in the store; cross-window theme sync; drop the now dead REMOVED_THEME_REMAP and Card.mode field. Tests: themeInjection (validator + sync), themeRegistry (cache/force/stale/ malformed), uninstallTheme (slot repair), migration notice. i18n in all nine locales. Full suite green (1755 tests). * test(bootstrap): cover startup theme apply + cross-window sync The review fixes added applyThemeAtStartup / installCrossWindowThemeSync to bootstrap.ts (a hot-path file) without tests, dropping its coverage to 68.3% and failing the frontend hot-path coverage gate (>=70%). Add unit tests for both (and the no-op / malformed-storage paths); bootstrap.ts is back to ~98%.
684 lines
60 KiB
TypeScript
684 lines
60 KiB
TypeScript
export const settings = {
|
||
title: 'Настройки',
|
||
language: 'Язык',
|
||
languageEn: 'English',
|
||
languageDe: 'Deutsch',
|
||
languageEs: 'Español',
|
||
languageFr: 'Français',
|
||
languageNl: 'Nederlands',
|
||
languageNb: 'Norsk',
|
||
languageRu: 'Русский',
|
||
languageZh: '中文',
|
||
languageRo: 'Română',
|
||
font: 'Шрифт',
|
||
fontHintOpenDyslexic: 'Подходит для дислексии · без поддержки китайского',
|
||
theme: 'Тема',
|
||
appearance: 'Оформление',
|
||
servers: 'Серверы',
|
||
serverName: 'Название',
|
||
serverUrl: 'Адрес',
|
||
serverUrlPlaceholder: '192.168.1.100:4533 или https://music.example.com',
|
||
serverAlternateUrl: 'Второй адрес (необязательно)',
|
||
serverAlternateUrlPlaceholderPublic: 'https://music.example.com',
|
||
serverAlternateUrlPlaceholderLocal: '192.168.1.100:4533',
|
||
serverAlternateUrlHintAddPublic: 'Добавьте публичный адрес, чтобы подключаться вне домашней сети.',
|
||
serverAlternateUrlHintAddLocal: 'Добавьте локальный адрес для более быстрой связи дома.',
|
||
serverBothLanError: 'Оба адреса локальные — оставьте один или добавьте публичный.',
|
||
dualAddressVerifying: 'Проверяем, что оба адреса указывают на один и тот же сервер…',
|
||
dualAddressMismatch: 'Адреса ведут к разным серверам. Проверьте написание и попробуйте снова.',
|
||
dualAddressInsufficient: 'Не удалось убедиться, что это один сервер. Удалите один адрес, чтобы продолжить.',
|
||
dualAddressUnreachable: 'Не удалось достучаться до {{host}}. Проверьте адрес и сеть, затем попробуйте снова.',
|
||
urlRemigrationTitle: 'Переместить данные библиотеки?',
|
||
urlRemigrationMessage: 'Смена адреса перенесёт все данные библиотеки, анализа и кэша обложек с {{oldKey}} на {{newKey}}. Это нельзя отменить.',
|
||
urlRemigrationConfirm: 'Переместить данные',
|
||
urlRemigrationProgress: 'Перемещаем данные библиотеки…',
|
||
urlRemigrationFailureInspect: 'Не удалось проверить, что нужно перенести. Попробуйте снова или откройте журнал отладки.',
|
||
urlRemigrationFailureRun: 'Не удалось перенести данные библиотеки. Сохранённые данные не изменены — попробуйте снова.',
|
||
urlRemigrationFailureCoverRename: 'Данные библиотеки перенесены, но файлы обложек не удалось переименовать. Обложки восстановятся при следующем обращении.',
|
||
shareUsesLocalUrl: 'Использовать локальный адрес в ссылках общего доступа',
|
||
shareUsesLocalUrlDesc: 'Применяется к приглашениям Orbit и ссылкам на библиотеку. По умолчанию — публичный адрес.',
|
||
serverUsername: 'Логин',
|
||
serverPassword: 'Пароль',
|
||
addServer: 'Добавить сервер',
|
||
addServerTitle: 'Новый сервер',
|
||
editServer: 'Изменить',
|
||
editServerTitle: 'Изменить сервер',
|
||
useServer: 'Выбрать',
|
||
deleteServer: 'Удалить',
|
||
noServers: 'Нет сохранённых серверов.',
|
||
serverActive: 'Активен',
|
||
confirmDeleteServer: 'Удалить сервер «{{name}}»?',
|
||
serverConnecting: 'Подключение…',
|
||
serverConnected: 'Подключено!',
|
||
serverFailed: 'Ошибка подключения.',
|
||
testBtn: 'Проверить',
|
||
testingBtn: 'Проверка…',
|
||
serverCompatible: 'Разработано для Navidrome. Другие Subsonic-совместимые серверы (Gonic, Airsonic, …) могут работать с ограничениями, так как Psysonic использует много специфичных для Navidrome API-эндпоинтов.',
|
||
userMgmtTitle: 'Управление пользователями',
|
||
userMgmtDesc: 'Управляйте пользователями этого сервера. Требуются права администратора.',
|
||
userMgmtNoAdmin: 'Для управления пользователями на этом сервере нужны права администратора.',
|
||
userMgmtLoadError: 'Не удалось загрузить пользователей.',
|
||
userMgmtLoadFriendly: 'Сервер не ответил — обычно разовая ошибка.',
|
||
userMgmtRetry: 'Повторить',
|
||
userMgmtEmpty: 'Пользователи не найдены.',
|
||
userMgmtYouBadge: 'Вы',
|
||
userMgmtAdminBadge: 'Админ',
|
||
userMgmtAddUser: 'Добавить пользователя',
|
||
userMgmtAddUserTitle: 'Новый пользователь',
|
||
userMgmtEditUserTitle: 'Изменить пользователя',
|
||
userMgmtUsername: 'Имя пользователя',
|
||
userMgmtName: 'Отображаемое имя',
|
||
userMgmtEmail: 'E-mail',
|
||
userMgmtPassword: 'Пароль',
|
||
userMgmtPasswordEditHint: 'Введите новый пароль, чтобы изменить его.',
|
||
userMgmtRoleAdmin: 'Админ',
|
||
userMgmtLibraries: 'Библиотеки',
|
||
userMgmtLibrariesAdminHint: 'Администраторы автоматически имеют доступ ко всем библиотекам.',
|
||
userMgmtLibrariesEmpty: 'На этом сервере нет доступных библиотек.',
|
||
userMgmtLibrariesValidation: 'Выберите хотя бы одну библиотеку.',
|
||
userMgmtLibrariesUpdateError: 'Пользователь сохранён, но не удалось назначить библиотеки',
|
||
userMgmtNoLibraries: 'Библиотеки не назначены',
|
||
userMgmtNeverSeen: 'Никогда',
|
||
userMgmtSave: 'Сохранить',
|
||
userMgmtCancel: 'Отмена',
|
||
userMgmtDelete: 'Удалить',
|
||
userMgmtEdit: 'Изменить',
|
||
userMgmtConfirmDelete: 'Удалить пользователя «{{username}}»? Действие нельзя отменить.',
|
||
userMgmtCreateError: 'Не удалось создать пользователя.',
|
||
userMgmtUpdateError: 'Не удалось обновить пользователя.',
|
||
userMgmtDeleteError: 'Не удалось удалить пользователя.',
|
||
userMgmtCreated: 'Пользователь создан.',
|
||
userMgmtUpdated: 'Пользователь обновлён.',
|
||
userMgmtDeleted: 'Пользователь удалён.',
|
||
userMgmtValidationMissing: 'Требуются имя пользователя, отображаемое имя и пароль.',
|
||
userMgmtValidationMissingIdentity: 'Укажите имя пользователя и отображаемое имя.',
|
||
userMgmtMagicStringGenerate: 'Сгенерировать magic string',
|
||
userMgmtSaveAndMagicString: 'Сохранить и получить magic string',
|
||
userMgmtMagicStringPasswordNavHint:
|
||
'Navidrome сохранит этот пароль для пользователя. Если он не совпадает с текущим, на сервере будет установлен новый пароль входа.',
|
||
userMgmtMagicStringPlaintextWarning:
|
||
'Передавайте magic string осторожно: в ней пароль в открытом виде (кодирование — не шифрование). У кого есть полная строка, тот может войти под этим пользователем.',
|
||
userMgmtMagicStringCopied: 'Magic string скопирован в буфер обмена.',
|
||
userMgmtMagicStringCopyFailed: 'Не удалось скопировать в буфер обмена.',
|
||
userMgmtMagicStringLoginFailed: 'Пароль не подошёл — не удалось проверить учётные данные.',
|
||
userMgmtMagicStringModalTitle: 'Сгенерировать magic string',
|
||
userMgmtMagicStringModalDesc: 'Введите пароль Subsonic для «{{username}}» — он попадёт в копируемую magic string.',
|
||
userMgmtMagicStringModalConfirm: 'Скопировать строку',
|
||
audiomuseTitle: 'AudioMuse-AI (Navidrome)',
|
||
audiomuseDesc:
|
||
'Включите, если на этом сервере настроен <pluginLink>плагин AudioMuse-AI для Navidrome</pluginLink>. Появится Instant Mix для треков, а на странице исполнителя похожие будут браться с сервера вместо Last.fm.',
|
||
audiomuseIssueHint:
|
||
'Недавно не удалось собрать Instant Mix — проверьте плагин Navidrome и API AudioMuse. Похожие исполнители подтянутся с Last.fm, если сервер ничего не вернёт.',
|
||
connected: 'Подключено',
|
||
failed: 'Ошибка',
|
||
eqTitle: 'Эквалайзер',
|
||
eqEnabled: 'Включить эквалайзер',
|
||
eqPreset: 'Пресет',
|
||
eqPresetCustom: 'Свой',
|
||
eqPresetBuiltin: 'Встроенные',
|
||
eqPresetCustomGroup: 'Мои пресеты',
|
||
eqSavePreset: 'Сохранить как пресет',
|
||
eqPresetName: 'Название пресета…',
|
||
eqDeletePreset: 'Удалить пресет',
|
||
eqResetBands: 'Сбросить полосы',
|
||
eqPreGain: 'Предусиление',
|
||
eqResetPreGain: 'Сбросить предусиление',
|
||
eqAutoEqTitle: 'AutoEQ — поиск профиля наушников',
|
||
eqAutoEqPlaceholder: 'Модель наушников или IEM…',
|
||
eqAutoEqSearching: 'Поиск…',
|
||
eqAutoEqNoResults: 'Ничего не найдено',
|
||
eqAutoEqError: 'Ошибка поиска',
|
||
eqAutoEqRateLimit: 'Лимит GitHub — попробуйте через минуту',
|
||
eqAutoEqFetchError: 'Не удалось загрузить профиль EQ',
|
||
lfmTitle: 'Last.fm',
|
||
lfmConnect: 'Подключить Last.fm',
|
||
lfmConnecting: 'Ожидание авторизации…',
|
||
lfmConfirm: 'Я разрешил доступ',
|
||
lfmConnected: 'Аккаунт',
|
||
lfmDisconnect: 'Отключить',
|
||
lfmConnectDesc:
|
||
'Привяжите Last.fm для скробблинга и статуса «Сейчас слушаю» прямо из Psysonic — настраивать Navidrome не нужно.',
|
||
lfmOpenBrowser: 'Откроется браузер. Разрешите доступ на Last.fm, затем нажмите кнопку ниже.',
|
||
lfmScrobbles: 'Скробблов: {{n}}',
|
||
lfmMemberSince: 'С {{year}} года',
|
||
scrobbleEnabled: 'Скробблинг включён',
|
||
scrobbleDesc: 'Отправлять прослушивания на Last.fm после 50% трека',
|
||
behavior: 'Поведение',
|
||
cacheTitle: 'Макс. размер кэша',
|
||
cacheDesc:
|
||
'Кэш обложек и фото исполнителей в хранилище приложения (IndexedDB). При нехватке места старые записи удаляются автоматически.',
|
||
cacheUsedImages: 'Изображения:',
|
||
cacheUsedOffline: 'Офлайн-треки:',
|
||
cacheUsedHot: 'На диске:',
|
||
hotCacheTrackCount: 'Треков в кэше:',
|
||
cacheMaxLabel: 'Лимит',
|
||
imageCacheScopeLabel: 'Область',
|
||
imageCacheSubTitle: 'Превью в приложении',
|
||
coverDiskCacheSubTitle: 'Обложки на диске',
|
||
coverCacheStrategyTitle: 'Кэш обложек',
|
||
coverCacheStrategyDesc: 'Для каждого сервера можно выбрать стратегию: Ленивый — загружать обложки по мере необходимости; Агрессивный — то же самое плюс дополнительный фоновый прогрев.',
|
||
coverCacheStrategyLabel: 'Стратегия',
|
||
coverCacheStrategyServerLabel: 'Сервер',
|
||
coverCacheStrategyProgressLabel: 'Кэш',
|
||
coverCacheStrategyActionsLabel: 'Действия',
|
||
coverCacheStrategyLazy: 'Ленивый',
|
||
coverCacheStrategyLazyDesc: 'Загружает обложки только когда они нужны.',
|
||
coverCacheStrategyAggressive: 'Агрессивный',
|
||
coverCacheStrategyAggressiveDesc: 'Как ленивый, но с дополнительным фоновым прогревом.',
|
||
coverCacheStrategyDiskUsage: 'На диске: {{size}}',
|
||
coverCacheStrategyProgressValue: '{{percent}}% ({{done}}/{{total}}) · {{size}}',
|
||
coverCacheStrategyClearAction: 'Очистить кэш обложек',
|
||
coverCacheStrategyClearTitle: 'Очистить кэш обложек?',
|
||
coverCacheStrategyClearDesc: 'Удалить все кэшированные обложки для {{server}}. При необходимости скачаются снова.',
|
||
coverCacheStrategyClearConfirm: 'Очистить',
|
||
coverCacheStrategyClearCancel: 'Отмена',
|
||
coverCacheStrategyClearSuccess: 'Кэш обложек для этого сервера очищен.',
|
||
coverCacheStrategyClearError: 'Не удалось очистить кэш обложек.',
|
||
coverCacheStrategyServerRemoved: 'удалённый сервер',
|
||
cacheClearBtn: 'Очистить кэш',
|
||
cacheClearWarning: 'Очистит кэш обложек и фото исполнителей в хранилище приложения. При необходимости скачаются снова.',
|
||
cacheClearConfirm: 'Очистить кэш изображений',
|
||
cacheClearCancel: 'Отмена',
|
||
waveformCacheCleared: 'Кэш waveform очищен ({{count}} строк).',
|
||
waveformCacheClearFailed: 'Не удалось очистить кэш waveform.',
|
||
storageTabTitle: 'Офлайн и кэш',
|
||
mediaDirTitle: 'Папка медиа',
|
||
mediaDirDesc: 'Корневая папка локального воспроизведения: офлайн-библиотека — library/, буфер очереди — cache/.',
|
||
mediaDirDefault: 'По умолчанию (данные приложения)',
|
||
mediaDirChange: 'Сменить папку',
|
||
mediaDirClear: 'Сбросить по умолчанию',
|
||
mediaDirHint: 'Новые загрузки пойдут в выбранную папку. Старые файлы останутся на прежних путях, пока вы их не удалите.',
|
||
offlineStorageTitle: 'Офлайн-хранилище',
|
||
offlineDirTitle: 'Офлайн-библиотека (в приложении)',
|
||
offlineDirDesc: 'Папка для треков, сохранённых для офлайн-прослушивания.',
|
||
offlineDirDefault: 'По умолчанию (данные приложения)',
|
||
offlineDirChange: 'Сменить папку',
|
||
offlineDirClear: 'Сбросить по умолчанию',
|
||
offlineDirHint: 'Новые загрузки пойдут в выбранную папку. Старые останутся на прежнем месте.',
|
||
hotCacheTitle: 'Горячий кэш воспроизведения',
|
||
hotCacheDisclaimer: 'Заранее подгружает следующие треки из очереди и сохраняет предыдущие. При включении использует место на диске и сеть.',
|
||
hotCacheDirDefault: 'По умолчанию (данные приложения)',
|
||
hotCacheDirChange: 'Сменить папку',
|
||
hotCacheDirClear: 'Сбросить по умолчанию',
|
||
hotCacheDirHint: 'При смене папки сбрасывается список в приложении; уже скачанные файлы остаются на старом пути, пока вы их не удалите.',
|
||
hotCacheEnabled: 'Включить горячий кэш воспроизведения',
|
||
hotCacheMaxMb: 'Максимальный размер кэша',
|
||
hotCacheDebounce: 'Задержка после изменения очереди',
|
||
hotCacheDebounceImmediate: 'Сразу',
|
||
hotCacheDebounceSeconds: '{{n}} с',
|
||
hotCacheClearBtn: 'Очистить горячий кэш',
|
||
audioOutputDevice: 'Устройство вывода звука',
|
||
audioOutputDeviceDesc: 'Выберите аудиоустройство для воспроизведения. Изменения применяются немедленно и перезапускают текущий трек.',
|
||
audioOutputDeviceDefault: 'Системное по умолчанию',
|
||
audioOutputDeviceRefresh: 'Обновить список устройств',
|
||
audioOutputDeviceOsDefaultNow: 'текущий системный вывод',
|
||
audioOutputDeviceListError: 'Не удалось загрузить список аудиоустройств.',
|
||
audioOutputDeviceNotInCurrentList: 'нет в текущем списке',
|
||
audioOutputDeviceMacNotice: 'В macOS воспроизведение в настоящее время всегда следует за системным устройством вывода по техническим причинам. Измените цель через Системные настройки → Звук или значок динамика в строке меню. Причина: CoreAudio запрашивает разрешение на использование микрофона при открытии не-стандартного потока — мы избегаем этого, всегда используя системный вывод по умолчанию.',
|
||
hiResTitle: 'Нативное воспроизведение Hi-Res',
|
||
hiResEnabled: 'Включить нативное Hi-Res воспроизведение',
|
||
hiResDesc: "По умолчанию вывод ограничен до 44,1 кГц для максимальной стабильности. Включайте только если оборудование и сеть надёжно поддерживают высокие частоты (88,2 кГц+).",
|
||
showArtistImages: 'Фото исполнителей',
|
||
showArtistImagesDesc:
|
||
'Показывать обложки в разделе «Исполнители». По умолчанию выключено — меньше нагрузки на диск и сеть.',
|
||
showOrbitTrigger: 'Показывать «Orbit» в шапке',
|
||
showOrbitTriggerDesc: 'Кнопка в шапке для запуска или присоединения к совместной сессии. Скрой, если не используешь Orbit — здесь же можно включить обратно.',
|
||
showTrayIcon: 'Иконка в трее',
|
||
showTrayIconDesc: 'Показывать Psysonic в области уведомлений / строке меню.',
|
||
minimizeToTray: 'Сворачивать в трей',
|
||
minimizeToTrayDesc: 'При закрытии окна не выходить из приложения, а оставаться в трее.',
|
||
clockFormat: 'Формат времени',
|
||
clockFormatDesc: 'Формат времени для ETA очереди и предпросмотра таймера сна.',
|
||
clockFormatAuto: 'Авто (системный)',
|
||
clockFormatTwentyFour: '24-часовой',
|
||
clockFormatTwelve: '12-часовой (AM/PM)',
|
||
preloadMiniPlayer: 'Предзагрузка мини-плеера',
|
||
preloadMiniPlayerDesc: 'Создаёт окно мини-плеера в фоне при запуске приложения, чтобы при первом открытии содержимое отображалось мгновенно. Использует немного больше памяти.',
|
||
useCustomTitlebar: 'Своя строка заголовка',
|
||
useCustomTitlebarDesc:
|
||
'Заменить системную строку заголовка встроенной, в стиле темы приложения. Отключите, чтобы использовать родную строку GNOME/GTK и т.д.',
|
||
linuxWebkitSmoothScroll: 'Плавное колесо (Linux)',
|
||
linuxWebkitSmoothScrollDesc: 'Вкл — инерция. Выкл — по шагам, как в GTK.',
|
||
linuxWebkitInputForceRepaint: 'Перерисовывать поля ввода при фокусе (Linux)',
|
||
linuxWebkitInputForceRepaintDesc: 'Обход проблемы WebKitGTK 2.50.x, когда текстовые поля зависают при клике. Иконки поиска кратко мигают при фокусе.',
|
||
linuxWaylandTextRender: 'Рендеринг текста Wayland (Linux)',
|
||
linuxWaylandTextRenderDesc:
|
||
'Сглаживание в интерфейсе меняется сразу. Политику ускорения WebKit (чёткий / GPU) сохраняем и применяем при следующем запуске — переключение на лету на части сборок зависает в WebKitGTK.',
|
||
linuxWaylandTextRenderBalanced: 'Сбалансированный',
|
||
linuxWaylandTextRenderSharp: 'Чёткий (меньше GPU)',
|
||
linuxWaylandTextRenderGpu: 'С приоритетом GPU',
|
||
linuxWaylandTextRenderMinimal: 'Минимальный (сглаживание по умолчанию)',
|
||
discordRichPresence: 'Статус в Discord',
|
||
discordRichPresenceDesc:
|
||
'Показывать текущий трек в профиле и статусе Discord. Нужен запущенный клиент Discord.',
|
||
discordCoverSource: 'Источник обложки',
|
||
discordCoverSourceDesc: 'Откуда загружать обложку альбома для профиля Discord.',
|
||
discordCoverNone: 'Нет (только иконка приложения)',
|
||
discordCoverServer: 'Сервер (через информацию об альбоме)',
|
||
discordCoverApple: 'Apple Music',
|
||
discordOptions: 'Расширенные параметры Discord',
|
||
discordTemplates: 'Пользовательские шаблоны текста',
|
||
discordTemplatesDesc: 'Настройте, какая информация отображается в профиле Discord. Переменные: {title}, {artist}, {album}',
|
||
discordTemplateDetails: 'Основная строка (details)',
|
||
discordTemplateState: 'Вторичная строка (state)',
|
||
discordTemplateLargeText: 'Подсказка альбома (largeText)',
|
||
discordTemplateName: 'Строка в списке пользователей (name)',
|
||
nowPlayingEnabled: 'Показывать в «Сейчас играет»',
|
||
nowPlayingEnabledDesc:
|
||
'Отправлять на сервер, что вы сейчас слушаете. Отключите, чтобы не делиться этим.',
|
||
enableBandsintown: 'Даты туров Bandsintown',
|
||
enableBandsintownDesc: 'Показывает предстоящие концерты текущего исполнителя на вкладке «Инфо». Данные берутся из публичного API Bandsintown.',
|
||
lyricsServerFirst: 'Предпочитать тексты с сервера',
|
||
lyricsServerFirstDesc: 'Проверять тексты песен, предоставленные сервером (встроенные теги, sidecar-файлы) перед запросом LRCLIB. Отключите, чтобы сначала использовать LRCLIB.',
|
||
enableNeteaselyrics: 'Тексты из Netease Cloud Music',
|
||
enableNeteaselyricsDesc: 'Использовать Netease Cloud Music как последний источник текстов, когда остальные способы не находят ничего. Лучшее покрытие для азиатской и международной музыки.',
|
||
lyricsSourcesTitle: 'Источники текстов',
|
||
lyricsSourcesDesc: 'Выберите источники для поиска текстов и их порядок. Перетаскивайте для сортировки. Отключённые источники пропускаются.',
|
||
lyricsSourceServer: 'Сервер',
|
||
lyricsSourceLrclib: 'LRCLIB',
|
||
lyricsSourceNetease: 'Netease Cloud Music',
|
||
lyricsYouLyPlus: 'YouLyPlus (Караоке)',
|
||
lyricsYouLyPlusDesc: 'Синхронизация слово-в-слово из общественного бэкенда (Apple Music, Spotify, Musixmatch, QQ). Пробуется первым; включённые источники ниже служат запасным вариантом.',
|
||
lyricsSourcesFallbackHint: 'Используются как запасной вариант, когда YouLyPlus ничего не находит. Перетащите для изменения порядка.',
|
||
lyricsSourcesPrimaryHint: 'Пробуются по порядку. Перетащите для изменения порядка. Всё выключено = текст отключён.',
|
||
lyricsStaticOnly: 'Показывать текст статично',
|
||
lyricsStaticOnlyDesc: 'Синхронизированный текст отображается без авто-прокрутки и подсветки слов.',
|
||
downloadsTitle: 'Экспорт ZIP и архивов',
|
||
downloadsFolderDesc: 'Куда сохранять альбомы в ZIP архиве на диск.',
|
||
downloadsDefault: 'Папка «Загрузки» по умолчанию',
|
||
pickFolder: 'Выбрать',
|
||
pickFolderTitle: 'Папка для загрузок',
|
||
clearFolder: 'Сбросить папку',
|
||
logout: 'Выйти',
|
||
aboutTitle: 'О Psysonic',
|
||
aboutDesc:
|
||
'Современный десктопный плеер, созданный для Navidrome. Использует API Subsonic плюс специфичные для Navidrome расширения. На Tauri v2 и нативном аудиодвижке на Rust — лёгкий и быстрый: волновая шкала, синхронные тексты, Last.fm, 10-полосный EQ, кроссфейд, воспроизведение без пауз, Replay Gain, жанры и много тем оформления.',
|
||
aboutLicense: 'Лицензия',
|
||
aboutLicenseText: 'GNU GPL v3 — свободное использование и изменение на тех же условиях.',
|
||
aboutRepo: 'Исходный код на GitHub',
|
||
aboutVersion: 'Версия',
|
||
aboutBuiltWith: 'Tauri · React · TypeScript · Rust/rodio',
|
||
aboutReleaseNotesLabel: 'Примечания к выпуску',
|
||
aboutReleaseNotesLink: 'Открыть «Что нового» этой версии',
|
||
aboutContributorsLabel: 'Участники',
|
||
showChangelogOnUpdate: 'Показывать «Что нового» после обновления',
|
||
showChangelogOnUpdateDesc: 'После обновления над «Сейчас играет» появится ненавязчивый баннер журнала изменений. Клик открывает заметки о выпуске, X скрывает.',
|
||
libraryGridMaxColumnsTitle: 'Сетки карточек в библиотеке',
|
||
libraryGridMaxColumnsPerfHint: 'Больше колонок — больше плиток в ряд и больше работы по вёрстке и отрисовке; на очень больших библиотеках или слабом железе это заметнее.',
|
||
libraryGridMaxColumnsRangeLabel: 'Максимум колонок ({{min}}–{{max}})',
|
||
libraryGridMaxColumnsDesc: 'Действует для альбомов, исполнителей, плейлистов, радио, офлайн-библиотеки и других экранов с карточками. Меньше колонок — крупнее плитки и обычно меньше нагрузка на CPU.',
|
||
libraryIndexTitle: 'Локальный индекс библиотеки (preview)',
|
||
libraryIndexDesc: 'Локальная копия каталога каждого сервера для быстрого поиска и офлайн-доступа. Первичная индексация для всех серверов; офлайн-серверы синхронизируются позже автоматически.',
|
||
libraryIndexDeltaHint: 'Фоновая дельта-синхронизация проверяет привязанные серверы каждые 30 с и запускается по расписанию — обычно раз в 5–45 минут в зависимости от размера библиотеки и нагрузки.',
|
||
libraryIndexEnable: 'Включить локальный индекс',
|
||
libraryIndexEnableAllDesc: 'Индексировать и синхронизировать все настроенные серверы в фоне.',
|
||
libraryIndexNoServer: 'Сначала добавьте сервер.',
|
||
libraryIndexServerListTitle: 'Индексируемые серверы',
|
||
libraryIndexAllExcluded: 'Все серверы исключены из синхронизации.',
|
||
libraryIndexServerOffline: 'Сервер офлайн — синхронизация отложена',
|
||
libraryIndexServerDeferred: 'Отложено',
|
||
libraryIndexServerSyncing: 'Синхронизация…',
|
||
libraryIndexFullResync: 'Полная пересинхронизация',
|
||
libraryIndexDeltaSync: 'Быстрая дельта',
|
||
libraryIndexExcludeServer: 'Исключить из синхронизации',
|
||
libraryIndexExcludingServer: 'Отключается…',
|
||
libraryIndexExcludedTitle: 'Исключены из синхронизации',
|
||
libraryIndexIncludeServer: 'Включить снова',
|
||
libraryIndexIncludingServer: 'Подключается…',
|
||
libraryIndexStatus: 'Статус',
|
||
libraryIndexStatusIdle: 'Ожидание',
|
||
libraryIndexStatusProbing: 'Проверка сервера…',
|
||
libraryIndexStatusInitial: 'Первичная синхронизация…',
|
||
libraryIndexStatusReady: 'Готово ({{count}} треков)',
|
||
libraryIndexStatusError: 'Ошибка — см. логи',
|
||
libraryIndexProgressIngest: 'Проиндексировано {{count}} треков…',
|
||
libraryIndexProgressVerify: 'Проверено {{checked}} (удалено {{deleted}})',
|
||
libraryIndexSyncNow: 'Синхронизировать',
|
||
libraryIndexVerify: 'Проверить целостность',
|
||
libraryIndexCancel: 'Отмена',
|
||
libraryIndexAutoReconcile: 'Авто-сверка при падении счётчика',
|
||
libraryIndexAutoReconcileDesc: 'Автоматически искать удалённые треки, когда на сервере их меньше ожидаемого.',
|
||
libraryIndexSyncError: 'Ошибка синхронизации: {{error}}',
|
||
libraryIndexBindError: 'Не удалось включить индекс: {{error}}',
|
||
analyticsStrategyTitle: 'Стратегии анализа',
|
||
analyticsStrategyDesc:
|
||
'Для каждого сервера можно выбрать стратегию: Ленивый — анализ по мере прослушивания; Агрессивный — фоновый анализ всей библиотеки.',
|
||
analyticsStrategyLabel: 'Стратегия',
|
||
analyticsStrategyLazy: 'Ленивая',
|
||
analyticsStrategyLazyDesc:
|
||
'Анализирует только треки, которые вы слушаете или которые уже попали в кэш.',
|
||
analyticsStrategyAdvanced: 'Агрессивный',
|
||
analyticsStrategyAdvancedDesc:
|
||
'Запускает фоновый анализ всей библиотеки и скачивает треки для анализа.',
|
||
analyticsStrategyPriorityTitle: 'Приоритет анализа (всегда)',
|
||
analyticsStrategyPriorityHigh: '1 — Сейчас играет: скачивание и анализ в первую очередь (байты воспроизведения, loudness backfill или HTTP при необходимости).',
|
||
analyticsStrategyPriorityMiddle: '2 — Следующие: ~5 треков в очереди и соседи hot cache — анализ из уже скачанных байтов, HTTP backfill только при промахе.',
|
||
analyticsStrategyPriorityLow: '3 — Библиотечный batch (только Агрессивный): автоматическая фоновая очередь в хвосте; пополняется по watermark (~3× потоков), не дожидаясь полного опустошения.',
|
||
analyticsStrategyServerLabel: 'Сервер',
|
||
analyticsStrategyProgressLabel: 'Прогресс анализа:',
|
||
analyticsStrategyProgressValue: '{{percent}}% ({{done}}/{{total}})',
|
||
analyticsStrategyActionsLabel: 'Действия',
|
||
analyticsStrategyParallelismLabel: 'Параллельные потоки',
|
||
analyticsStrategyParallelismValue: '{{n}} потоков',
|
||
analyticsStrategyParallelismDesc: 'Сколько треков одновременно скачивать и анализировать (HTTP + CPU). Больше значение — быстрее догоняет библиотеку, но выше нагрузка на сеть и CPU.',
|
||
analyticsStrategyAdvancedWarning:
|
||
'Агрессивный режим скачивает всю библиотеку для анализа и создаёт заметную нагрузку на сеть и процессор. Рекомендуется в локальной сети или для не очень больших библиотек.',
|
||
analyticsStrategyServerRemoved: 'Удалённый сервер',
|
||
analyticsStrategyClearAction: 'Очистить анализ',
|
||
analyticsStrategyClearTitle: 'Очистить анализ?',
|
||
analyticsStrategyClearDesc: 'Очистка анализа для {{server}} удалит кэш волн и loudness. При повторном добавлении сервера индексация может занять много времени.',
|
||
analyticsStrategyClearCancel: 'Отмена',
|
||
analyticsStrategyClearConfirm: 'Очистить анализ',
|
||
analyticsStrategyClearSuccess: 'Анализ удалён для удалённого сервера.',
|
||
analyticsStrategyClearError: 'Не удалось очистить анализ для удалённого сервера.',
|
||
analyticsStrategyProgressEmptyAfterFailed: '100% (0/0, треки помечены как проблемные)',
|
||
analyticsFailedTracksOpenTitle: 'Проблемных треков: {{count}}',
|
||
analyticsFailedTracksTitle: 'Проблемные треки анализа — {{server}}',
|
||
analyticsFailedTracksDesc: 'Эти треки многократно падали на decode/enrichment и исключены из фонового автоанализа.',
|
||
analyticsFailedTracksLoading: 'Загрузка списка проблемных треков…',
|
||
analyticsFailedTracksEmpty: 'Для этого сервера проблемных треков нет.',
|
||
analyticsFailedTracksClose: 'Закрыть',
|
||
analyticsFailedTracksExport: 'Экспорт списка',
|
||
analyticsFailedTracksExportSuccess: 'Список проблемных треков экспортирован ({{count}}).',
|
||
analyticsFailedTracksExportError: 'Не удалось экспортировать список проблемных треков.',
|
||
analyticsFailedTracksRescan: 'Рескан проблемных треков',
|
||
analyticsFailedTracksRescanSuccess: 'Рескан запланирован для {{count}} проблемных треков.',
|
||
analyticsFailedTracksRescanError: 'Не удалось запустить рескан проблемных треков.',
|
||
analyticsFailedTracksLoadError: 'Не удалось загрузить список проблемных треков.',
|
||
randomMixTitle: 'Чёрный список случайного микса',
|
||
luckyMixMenuTitle: 'Показывать «Мне повезёт» в меню',
|
||
luckyMixMenuDesc:
|
||
'Включает «Мне повезёт» в «Собрать микс», а при раздельной навигации — отдельным пунктом меню. Видно только при включённом AudioMuse на активном сервере.',
|
||
randomMixBlacklistTitle: 'Свои слова-фильтры',
|
||
randomMixBlacklistDesc:
|
||
'Треки скрываются, если слово встречается в жанре, названии, альбоме или исполнителе (когда включён фильтр выше).',
|
||
randomMixBlacklistPlaceholder: 'Добавить слово…',
|
||
randomMixBlacklistAdd: 'Добавить',
|
||
randomMixBlacklistEmpty: 'Пока пусто.',
|
||
randomMixHardcodedTitle: 'Встроенные слова (при включённом фильтре)',
|
||
tabAudio: 'Звук',
|
||
tabStorage: 'Офлайн и кэш',
|
||
tabAppearance: 'Внешний вид',
|
||
tabThemes: 'Темы',
|
||
themesYourThemesTitle: 'Ваши темы',
|
||
themesCvdTooltip: 'Безопасно при дальтонизме — дейтеранопия, протанопия, тританопия',
|
||
themeStoreTitle: 'Магазин тем',
|
||
themeStoreSubmitText: 'Создали свою тему? Поделитесь с сообществом — подробности в репозитории тем.',
|
||
themeStoreSubmitLink: 'Открыть репозиторий тем',
|
||
themeStoreSearchPlaceholder: 'Поиск тем…',
|
||
themeStoreFilterMode: 'Фильтр по режиму',
|
||
themeStoreModeAll: 'Все',
|
||
themeStoreModeDark: 'Тёмные',
|
||
themeStoreModeLight: 'Светлые',
|
||
themeStoreRefresh: 'Обновить',
|
||
themeStoreLoading: 'Загрузка тем…',
|
||
themeStoreError: 'Не удалось загрузить магазин тем. Проверьте подключение.',
|
||
themeStoreRetry: 'Повторить',
|
||
themeStoreEmpty: 'Нет тем, соответствующих фильтрам.',
|
||
themeStoreActive: 'Активна',
|
||
themeStoreEnlarge: 'Увеличить превью',
|
||
themeStoreByAuthor: 'от {{author}}',
|
||
themeMigrationNoticeTitle: 'Тема перенесена в магазин',
|
||
themeMigrationNoticeBody: '{{themes}} больше не входит в приложение. Вы можете переустановить её из магазина тем (Настройки → Темы) или выбрать другую тему.',
|
||
themeMigrationNoticeOpen: 'Открыть магазин тем',
|
||
themeStoreOffline: 'Показан последний кэшированный каталог — похоже, вы офлайн.',
|
||
themeStoreInstall: 'Установить',
|
||
themeStoreInstalling: 'Установка…',
|
||
themeStoreApply: 'Применить',
|
||
themeStoreUpdate: 'Обновить',
|
||
themeStoreUpdating: 'Обновление…',
|
||
themeStoreUninstall: 'Удалить',
|
||
themeStoreInstallFailed: 'Ошибка установки',
|
||
tabLibrary: 'Библиотека',
|
||
tabServers: 'Серверы',
|
||
tabLyrics: 'Тексты песен',
|
||
tabPersonalisation: 'Персонализация',
|
||
tabIntegrations: 'Интеграции',
|
||
inputKeybindingsTitle: 'Горячие клавиши',
|
||
aboutContributorsCount_one: '{{count}} вклад',
|
||
aboutContributorsCount_few: '{{count}} вклада',
|
||
aboutContributorsCount_many: '{{count}} вкладов',
|
||
aboutContributorsCount_other: '{{count}} вклада',
|
||
searchPlaceholder: 'Поиск в настройках…',
|
||
searchNoResults: 'Ничего не найдено по вашему запросу.',
|
||
aboutMaintainersLabel: 'Сопровождающие',
|
||
integrationsPrivacyTitle: 'Уведомление о конфиденциальности',
|
||
integrationsPrivacyBody: 'Все интеграции на этой вкладке включаются <strong>по желанию</strong> и при активации отправляют данные во внешние сервисы или на ваш сервер Navidrome. Last.fm получает вашу историю прослушивания, Discord показывает текущую композицию в вашем профиле, Bandsintown опрашивается по артисту для получения дат гастролей, а «Сейчас играет» делится текущей композицией с другими пользователями вашего сервера Navidrome. Если вы не хотите ничего из этого, просто оставьте соответствующий раздел отключённым.',
|
||
homeCustomizerTitle: 'Для вас',
|
||
queueModeTitle: 'Режим отображения очереди',
|
||
queueModeQueueSub: 'Показывает только предстоящие треки. Текущий трек остаётся в заголовке и исчезает из списка после воспроизведения.',
|
||
queueModePlaylistSub: 'Сохраняет всю очередь в списке, текущий трек выделен вверху; воспроизведённые треки остаются.',
|
||
queueModeTimelineSub: 'Центрировать текущий трек: история воспроизведения сверху, предстоящие треки (в порядке перемешивания) снизу.',
|
||
queueToolbarTitle: 'Панель инструментов очереди',
|
||
queueToolbarReset: 'Сбросить',
|
||
queueToolbarSeparator: 'Разделитель',
|
||
sidebarTitle: 'Боковая панель',
|
||
sidebarReset: 'Сбросить порядок',
|
||
artistLayoutTitle: 'Разделы страницы исполнителя',
|
||
artistLayoutDesc: 'Перетаскивайте, чтобы изменить порядок, переключайте, чтобы скрыть отдельные разделы страницы исполнителя. Разделы без данных пропускаются автоматически.',
|
||
artistLayoutReset: 'Сбросить',
|
||
artistLayoutBio: 'Биография исполнителя',
|
||
artistLayoutTopTracks: 'Топ-треки',
|
||
artistLayoutSimilar: 'Похожие исполнители',
|
||
artistLayoutAlbums: 'Альбомы',
|
||
artistLayoutFeatured: 'Также участвует в',
|
||
playlistLayoutTitle: 'Макет страницы плейлиста',
|
||
playlistLayoutDesc: 'Показать или скрыть отдельные элементы на странице плейлиста.',
|
||
playlistLayoutReset: 'Сбросить',
|
||
playerBarTitle: 'Панель плеера',
|
||
playerBarReset: 'Сбросить',
|
||
playerBarStarRating: 'Оценка звёздами',
|
||
playerBarFavorite: 'Избранное (сердечко)',
|
||
playerBarLastfmLove: 'Last.fm love',
|
||
playerBarPlaybackRate: 'Скорость',
|
||
playerBarEqualizer: 'Эквалайзер',
|
||
playerBarMiniPlayer: 'Мини-плеер',
|
||
playbackRateTitle: 'Скорость воспроизведения',
|
||
playbackRateEnabled: 'Включить изменение скорости',
|
||
playbackRateEnabledDesc: 'Глобальная скорость для всех треков. Не применяется к радио и превью.',
|
||
playbackRateStrategy: 'Стратегия',
|
||
playbackRateStrategySpeed: 'Скорость',
|
||
playbackRateStrategyVarispeed: 'С тоном',
|
||
playbackRateStrategyPreserve: 'Сдвиг тона',
|
||
playbackRateSpeed: 'Скорость',
|
||
playbackRatePitch: 'Тон',
|
||
playbackRateDerivedPitch: 'Сдвиг тона от скорости: {{value}}',
|
||
playbackRateAutoPitch: 'Тон корректируется автоматически.',
|
||
playbackRateHint: '«Скорость» сохраняет высоту тона при изменении темпа. «С тоном» меняет тон вместе со скоростью. «Сдвиг тона» — ручное смещение. Выше нагрузка на процессор, чем у режима «С тоном». Не для радио, превью и Orbit.',
|
||
playbackRateNeutral: 'При 1.0× и нулевом сдвиге тона воспроизведение обычное.',
|
||
playbackRateOrbitPaused: 'Во время сессии Orbit не применяется — воспроизведение остаётся 1.0× для синхронизации.',
|
||
playbackRateOrbitPausedShort: 'Orbit: 1.0× для синхронизации.',
|
||
advancedMode: 'Дополнительно',
|
||
advancedModeTooltip: 'Показывать расширенные параметры во всех вкладках настроек. Вклады сообщества, которые не обязательно соответствуют дизайн-философии мейнтейнеров Psysonic.',
|
||
advancedBadge: 'Дополнительно',
|
||
sidebarDrag: 'Перетащите для порядка',
|
||
sidebarFixed: 'Всегда показывать',
|
||
nowPlayingTopTitle: '«Сейчас играет» вверху',
|
||
nowPlayingTopDesc: 'Закрепить пункт «Сейчас играет» вверху боковой панели вместо нижней части.',
|
||
randomNavSplitTitle: 'Разделить навигацию микса',
|
||
randomNavSplitDesc:
|
||
'Показывать «Случайный микс», «Случайные альбомы» и «Мне повезёт» как отдельные пункты боковой панели вместо хаба «Собрать микс».',
|
||
tabInput: 'Ввод',
|
||
tabUsers: 'Пользователи',
|
||
tabSystem: 'Система',
|
||
loggingTitle: 'Логирование',
|
||
loggingModeDesc: 'Управляет подробностью логов backend в терминале.',
|
||
loggingModeOff: 'Выключено',
|
||
loggingModeNormal: 'Обычное',
|
||
loggingModeDebug: 'Дебаг',
|
||
loggingExport: 'Экспорт логов',
|
||
loggingExportSuccess: 'Логи экспортированы ({{count}} строк).',
|
||
loggingExportError: 'Не удалось экспортировать логи.',
|
||
ratingsSectionTitle: 'Рейтинги',
|
||
ratingsSkipStarTitle: 'Скипнуть для 1 звезды',
|
||
ratingsSkipStarDesc:
|
||
'При N скипов подряд ставить 1★ треку. Только для не оцененных ранее.',
|
||
ratingsSkipStarThresholdLabel: 'Скипов',
|
||
ratingsMixFilterTitle: 'Фильтрация по рейтингу',
|
||
ratingsMixFilterDesc:
|
||
'Фильтровать с низким рейтингом в «{{mix}}» и «{{albums}}». Повторный клик по выбранной звезде отключает порог.',
|
||
ratingsMixMinSong: 'Песни',
|
||
ratingsMixMinAlbum: 'Альбомы',
|
||
ratingsMixMinArtist: 'Исполнители',
|
||
ratingsMixMinThresholdAria: 'Минимум звёзд: {{label}}',
|
||
backupTitle: 'Резервная копия',
|
||
backupExport: 'Экспорт настроек',
|
||
backupExportDesc:
|
||
'Сохраняет настройки, серверы, Last.fm, тему, EQ и горячие клавиши в файл .psybkp. Пароли в открытом виде — храните файл в безопасности.',
|
||
backupImport: 'Импорт настроек',
|
||
backupImportDesc: 'Восстановить из .psybkp. Приложение перезапустится.',
|
||
backupImportConfirm: 'Текущие настройки будут заменены. Продолжить?',
|
||
backupImportAny: 'Импорт резервной копии',
|
||
backupImportAnyConfirm:
|
||
'Импортировать выбранный файл резервной копии и применить его содержимое (настройки, базы библиотеки или полный архив). Продолжить?',
|
||
backupSuccess: 'Копия сохранена',
|
||
backupImportSuccess: 'Настройки восстановлены — перезапуск…',
|
||
backupImportError: 'Файл повреждён или не подходит.',
|
||
backupModeFull: 'Полный',
|
||
backupModeLibrary: 'Базы библиотеки',
|
||
backupModeConfig: 'Настройки',
|
||
backupFullDesc: 'Архивирует настройки и базы библиотеки в один файл резервной копии.',
|
||
backupFullExport: 'Экспорт полного архива',
|
||
backupFullImport: 'Импорт полного архива',
|
||
backupFullImportConfirm:
|
||
'Настройки будут перезаписаны, текущие базы библиотеки будут заменены и сохранены в .bak. Продолжить?',
|
||
backupFullExportSuccess: 'Полный архив экспортирован.',
|
||
backupFullImportSuccess: 'Полный архив импортирован — перезапуск…',
|
||
backupFullImportError: 'Не удалось импортировать полный архив.',
|
||
backupLibraryExport: 'Экспорт баз',
|
||
backupLibraryExportDesc: 'Создаёт сжатый архив с чистыми SQLite-снимками баз библиотеки.',
|
||
backupLibraryImport: 'Импорт баз',
|
||
backupLibraryImportDesc:
|
||
'Восстанавливает базы библиотеки из архива. Текущие базы перед заменой переносятся в .bak.',
|
||
backupLibraryImportConfirm:
|
||
'Текущие базы библиотеки будут заменены и сохранены в .bak. Продолжить?',
|
||
backupLibraryExportSuccess: 'Копия баз экспортирована.',
|
||
backupLibraryImportSuccess: 'Копия баз импортирована.',
|
||
backupLibraryImportError: 'Не удалось импортировать копию баз.',
|
||
backupOverlayExportTitle: 'Создаём резервную копию…',
|
||
backupOverlayImportTitle: 'Восстанавливаем резервную копию…',
|
||
backupOverlayHint: 'Окно можно не закрывать, идёт обработка файлов. Ввод временно заблокирован.',
|
||
shortcutsReset: 'Сбросить',
|
||
shortcutListening: 'Нажмите клавишу…',
|
||
shortcutUnbound: '—',
|
||
globalShortcutsTitle: 'Глобальные сочетания',
|
||
globalShortcutsNote:
|
||
'Работают, когда окно не в фокусе. Нужен модификатор: Ctrl, Alt или Super.',
|
||
shortcutClear: 'Сбросить',
|
||
shortcutPlayPause: 'Пауза / воспроизведение',
|
||
shortcutNext: 'Следующий трек',
|
||
shortcutPrev: 'Предыдущий трек',
|
||
shortcutVolumeUp: 'Громче',
|
||
shortcutVolumeDown: 'Тише',
|
||
shortcutSeekForward: 'Вперёд на 10 с',
|
||
shortcutSeekBackward: 'Назад на 10 с',
|
||
shortcutToggleQueue: 'Показать / скрыть очередь',
|
||
shortcutOpenFolderBrowser: 'Открыть {{folderBrowser}}',
|
||
shortcutFullscreenPlayer: 'Полноэкранный плеер',
|
||
shortcutNativeFullscreen: 'Системный полный экран',
|
||
shortcutOpenMiniPlayer: 'Открыть мини-плеер',
|
||
shortcutStartSearch: 'Начать поиск',
|
||
shortcutStartAdvancedSearch: 'Начать расширенный поиск',
|
||
shortcutToggleSidebar: 'Переключить боковую панель',
|
||
shortcutMuteSound: 'Без звука',
|
||
shortcutToggleEqualizer: 'Открыть / переключить эквалайзер',
|
||
shortcutToggleRepeat: 'Переключить повтор',
|
||
shortcutOpenNowPlaying: 'Открыть «Сейчас играет»',
|
||
shortcutShowLyrics: 'Показать текст песни',
|
||
shortcutFavoriteCurrentTrack: 'Добавить текущий трек в избранное',
|
||
shortcutOpenHelp: 'Справка',
|
||
playbackTitle: 'Воспроизведение',
|
||
replayGain: 'Replay Gain',
|
||
replayGainDesc: 'Выравнивание громкости по метаданным ReplayGain',
|
||
replayGainMode: 'Режим',
|
||
replayGainAuto: 'Авто',
|
||
replayGainAutoDesc: 'Использует громкость альбома, когда соседние треки в очереди из того же альбома, иначе громкость трека.',
|
||
replayGainTrack: 'По треку',
|
||
replayGainAlbum: 'По альбому',
|
||
replayGainPreGain: 'Предусиление (файлы с тегами)',
|
||
replayGainPreGainDesc: 'Общая прибавка, добавляемая к каждому расчёту ReplayGain. Удобно, если библиотека в целом кажется тихой.',
|
||
replayGainFallback: 'Резерв (без тегов / радио)',
|
||
replayGainFallbackDesc: 'Применяется к трекам (и радиопотокам) без тегов ReplayGain, чтобы непомеченный контент не звучал громче остального.',
|
||
normalization: 'Нормализация',
|
||
normalizationDesc: 'Выровнять воспринимаемую громкость между треками, альбомами и радио.',
|
||
normalizationOff: 'Выкл.',
|
||
normalizationReplayGain: 'ReplayGain',
|
||
normalizationLufs: 'LUFS',
|
||
loudnessTargetLufs: 'Целевой LUFS',
|
||
loudnessTargetLufsDesc: 'Опорная громкость, к которой подгоняются все треки. Меньше число = громче на выходе. Стриминг-сервисы обычно держат около -14 LUFS.',
|
||
loudnessPreAnalysisAttenuation: 'Ослабление до измерения',
|
||
loudnessPreAnalysisAttenuationDesc:
|
||
'Насколько приглушить звук, пока для трека нет сохранённого измерения громкости. При стриме дальше идут лишь грубые оценки, не полный LUFS. 0 dB — без дополнительного приглушения; ниже — тише. Справа показывается фактическая поправка для текущей цели.',
|
||
loudnessPreAnalysisAttenuationRef: 'Фактически {{eff}} dB при цели {{tgt}} LUFS.',
|
||
loudnessPreAnalysisAttenuationReset: 'По умолчанию',
|
||
loudnessFirstPlayNote:
|
||
'При первом прослушивании нового трека громкость может ненадолго колебаться, пока идёт измерение. Со второго раза применяется уже сохранённое значение и всё стабильно. Следующие треки в очереди обычно анализируются во время предыдущей композиции, поэтому на практике это случается редко.',
|
||
crossfade: 'Кроссфейд',
|
||
crossfadeDesc: 'Плавный переход между треками',
|
||
crossfadeSecs: '{{n}} с',
|
||
notWithGapless: 'Недоступно при включённом режиме без пауз',
|
||
notWithCrossfade: 'Недоступно при включённом кроссфейде',
|
||
gapless: 'Без пауз между треками',
|
||
gaplessDesc: 'Заранее подгружать следующий трек, чтобы не было тишины',
|
||
preservePlayNextOrder: 'Сохранять порядок «Играть следующим»',
|
||
preservePlayNextOrderDesc: 'Новые элементы «Играть следующим» становятся в конец очереди, а не лезут вперёд.',
|
||
trackPreviewsTitle: 'Превью треков',
|
||
trackPreviewsToggle: 'Включить превью треков',
|
||
trackPreviewsDesc: 'Показывать встроенные кнопки воспроизведения и превью в списках треков для быстрого прослушивания фрагмента.',
|
||
trackPreviewStart: 'Начальная позиция',
|
||
trackPreviewStartDesc: 'С какого момента трека начинается превью (% от длительности).',
|
||
trackPreviewDuration: 'Длительность',
|
||
trackPreviewDurationDesc: 'Сколько играет каждое превью до автоматической остановки.',
|
||
trackPreviewDurationSecs: '{{n}} с',
|
||
trackPreviewLocationsTitle: 'Где показывать превью',
|
||
trackPreviewLocationsDesc: 'Выберите, в каких списках показывать кнопки превью.',
|
||
trackPreviewLocation_suggestions: 'В предложениях плейлиста',
|
||
trackPreviewLocation_albums: 'В списках треков альбома',
|
||
trackPreviewLocation_playlists: 'В плейлистах',
|
||
trackPreviewLocation_favorites: 'В избранном',
|
||
trackPreviewLocation_artist: 'В топ-треках исполнителя',
|
||
trackPreviewLocation_randomMix: 'В Random Mix',
|
||
nextTrackBufferingTitle: 'Буферизация',
|
||
infiniteQueue: 'Бесконечная очередь',
|
||
infiniteQueueDesc: 'Подмешивать случайные треки, когда очередь закончится',
|
||
experimental: 'Экспериментально',
|
||
sidebarLyricsStyle: 'Стиль прокрутки текста',
|
||
sidebarLyricsStyleClassic: 'Классический',
|
||
sidebarLyricsStyleClassicDesc: 'Активная строка центрируется.',
|
||
sidebarLyricsStyleApple: 'Apple Music-like',
|
||
sidebarLyricsStyleAppleDesc: 'Активная строка фиксируется сверху с плавной анимацией.',
|
||
seekbarStyle: 'Стиль прогресс-бара',
|
||
seekbarStyleDesc: 'Выбор внешнего вида полосы воспроизведения',
|
||
seekbarTruewave: 'Реальная форма волны',
|
||
seekbarPseudowave: 'Псевдо форма волны',
|
||
seekbarLinedot: 'Линия и точка',
|
||
seekbarBar: 'Полоса',
|
||
seekbarThick: 'Толстая полоса',
|
||
seekbarSegmented: 'Сегменты',
|
||
seekbarNeon: 'Неон',
|
||
seekbarPulsewave: 'Пульс-волна',
|
||
seekbarParticletrail: 'Частицы',
|
||
seekbarLiquidfill: 'Жидкость',
|
||
seekbarRetrotape: 'Ретро-лента',
|
||
themeSchedulerTitle: 'Расписание тем',
|
||
themeSchedulerEnable: 'Включить расписание тем',
|
||
themeSchedulerEnableSub: 'Автоматически переключается между двумя темами в зависимости от времени суток',
|
||
themeSchedulerDayTheme: 'Дневная тема',
|
||
themeSchedulerDayStart: 'День начинается в',
|
||
themeSchedulerNightTheme: 'Ночная тема',
|
||
themeSchedulerNightStart: 'Ночь начинается в',
|
||
themeSchedulerActiveHint: 'Расписание тем активно - темы переключаются автоматически.',
|
||
visualOptionsTitle: 'Визуальные Настройки',
|
||
coverArtBackground: 'Фон Обложки',
|
||
coverArtBackgroundSub: 'Показывать размытую обложку как фон в заголовках альбомов и плейлистов',
|
||
playlistCoverPhoto: 'Обложка Плейлиста',
|
||
playlistCoverPhotoSub: 'Показывать сетку обложек в детальном виде плейлиста',
|
||
showBitrate: 'Показывать Битрейт',
|
||
showBitrateSub: 'Отображать битрейт аудио в списках треков',
|
||
floatingPlayerBar: 'Плавающая панель плеера',
|
||
floatingPlayerBarSub: 'Держать панель плеера плавающей над содержимым',
|
||
uiScaleTitle: 'Масштаб интерфейса',
|
||
uiScaleLabel: 'Масштаб',
|
||
confirmDeleteServerLibrary: 'Также удалить локальный индекс библиотеки этого сервера? Нажмите «Отмена», чтобы сохранить кэш-копию для офлайн-использования.',
|
||
};
|