diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc1b5eb..00854b22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -147,6 +147,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **Settings → Personalisation** gains a **Queue Settings** category that brings the queue display mode, the queue toolbar customizer, and the **Preserve "Play Next" order** toggle (moved here from Audio) together in one place. * On macOS, the **Audio Output Device** category is now hidden rather than showing a notice — playback there always follows the system output device. +### Russian locale — missing strings and phrasing cleanup + +**By [@kilyabin](https://github.com/kilyabin), PR [#1181](https://github.com/Psychotoxical/psysonic/pull/1181)** + +* Fifty strings that still fell back to English in the Russian UI are now translated — macOS in-place updater, device sync file migration, fullscreen lyrics, and statistics share-image export. +* User-facing descriptions in Russian and English no longer mention WebKitGTK or Fisher–Yates internals; several Russian labels and section titles read more naturally (settings casing, smart playlists, track transitions, and home rails). + ## Fixed diff --git a/src/config/settingsCredits.ts b/src/config/settingsCredits.ts index 636ca77f..0e5b8344 100644 --- a/src/config/settingsCredits.ts +++ b/src/config/settingsCredits.ts @@ -196,6 +196,7 @@ const CONTRIBUTOR_ENTRIES = [ 'Golos Text and Unbounded fonts with Cyrillic support (PR #206)', 'Fullscreen & sidebar lyrics: duration-based ease-out scroll animator replacing spring physics; bottom fade for plain lyrics (PR #214)', 'Sidebar lyrics: YouLy+ source strings render in a single line (PR #215)', + 'Russian locale — 50 missing strings and ru/en phrasing cleanup (PR #1181)', ], }, { diff --git a/src/locales/en/orbit.ts b/src/locales/en/orbit.ts index cd08d81a..8e45e57b 100644 --- a/src/locales/en/orbit.ts +++ b/src/locales/en/orbit.ts @@ -134,7 +134,7 @@ export const orbit = { settingAutoApprove: 'Auto-approve suggestions', settingAutoApproveHint: "Guest suggestions land in your queue instantly. Off: they stay in the session list for you to review later.", settingAutoShuffle: 'Automatic reshuffle', - settingAutoShuffleHint: "Periodic Fisher–Yates shuffle of the upcoming queue. Off: order only changes when you rearrange it.", + settingAutoShuffleHint: "Periodically reshuffles the upcoming queue. Off: order only changes when you rearrange it manually.", settingShuffleInterval: 'Reshuffle every', settingShuffleIntervalHint: 'How often the upcoming queue gets reshuffled while the session runs.', suggestBlockedMuted: 'The host muted you for this session — suggestions are off.', diff --git a/src/locales/en/settings.ts b/src/locales/en/settings.ts index 8eb28f5e..db6c2fea 100644 --- a/src/locales/en/settings.ts +++ b/src/locales/en/settings.ts @@ -278,10 +278,10 @@ export const settings = { linuxWebkitSmoothScroll: 'Smooth wheel (Linux)', linuxWebkitSmoothScrollDesc: 'On: inertial scroll. Off: line-by-line, GTK-style.', linuxWebkitInputForceRepaint: 'Repaint inputs on focus (Linux)', - linuxWebkitInputForceRepaintDesc: 'Workaround for WebKitGTK 2.50.x where text fields freeze when clicked. Search icons flicker briefly on focus.', + linuxWebkitInputForceRepaintDesc: 'Fixes text fields that freeze when clicked. Search icons may flicker briefly on focus.', linuxWaylandTextRender: 'Wayland text rendering (Linux)', linuxWaylandTextRenderDesc: - 'Font smoothing in the UI updates immediately. WebKit hardware acceleration (sharp / GPU presets) is saved and applied on the next app start — changing it live can freeze WebKitGTK on some setups.', + 'Font smoothing updates immediately. The acceleration preset (Sharp / GPU) takes effect after restarting the app.', linuxWaylandTextRenderBalanced: 'Balanced', linuxWaylandTextRenderSharp: 'Sharp (CPU-friendly)', linuxWaylandTextRenderGpu: 'GPU-first', diff --git a/src/locales/ru/common.ts b/src/locales/ru/common.ts index 519425f8..e58606d5 100644 --- a/src/locales/ru/common.ts +++ b/src/locales/ru/common.ts @@ -9,6 +9,7 @@ export const common = { downloadZip: 'Скачать (ZIP)', back: 'Назад', backToTop: 'Наверх', + close: 'Закрыть', cancel: 'Отмена', save: 'Сохранить', delete: 'Удалить', @@ -49,6 +50,15 @@ export const common = { updaterAurHint: 'Установить обновление через AUR:', updaterErrorMsg: 'Ошибка загрузки', updaterRetryBtn: 'Повторить', + updaterInstallNow: 'Установить сейчас', + updaterMacReadyTitle: 'Готово к установке', + updaterMacReady: 'Обновление загружается, проверяется и применяется автоматически — DMG не нужен. После завершения приложение перезапустится само.', + updaterTrustNotarized: 'Нотаризовано Apple', + updaterTrustSignature: 'Подпись проверена', + updaterMacDoneTitle: 'Обновление установлено', + updaterRestartingIn: 'Перезапуск через {{n}} с…', + updaterRestarting: 'Перезапуск…', + updaterRestartNow: 'Перезапустить сейчас', durationHoursMinutes: '{{hours}}ч {{minutes}}мин', durationMinutesOnly: '{{minutes}}мин', updaterOpenGitHub: 'Открыть на GitHub', diff --git a/src/locales/ru/deviceSync.ts b/src/locales/ru/deviceSync.ts index 12400ecb..df5104c1 100644 --- a/src/locales/ru/deviceSync.ts +++ b/src/locales/ru/deviceSync.ts @@ -9,8 +9,25 @@ export const deviceSync = { free: 'свободно', notMountedVolume: 'Цель не находится на смонтированном томе. Диск мог быть отключён.', chooseFolder: 'Выбрать…', - filenameTemplate: 'Шаблон имени файла', targetDevice: 'Целевое устройство', + schemaLabel: 'Схема именования', + schemaHint: 'Фиксированная схема для надёжной синхронизации между ОС. Плейлисты сохраняются как .m3u8 со ссылками на треки альбома — дубликатов на устройстве не будет.', + migrateButton: 'Реорганизовать существующие файлы…', + migrateTooltip: 'Переименовать существующие файлы на устройстве по новой схеме (со старого шаблона имён).', + migrateTitle: 'Реорганизация существующих файлов', + migrateLoading: 'Анализ существующих файлов…', + migrateNothingToDo: 'Все файлы уже соответствуют новой схеме — делать нечего.', + migrateNoTemplate: 'На устройстве не найден старый шаблон имён. Миграция применяется только если устройство синхронизировалось версией Psysonic с поддержкой пользовательских шаблонов.', + migrateFilesToRename: 'файлов будет переименовано', + migrateUnchanged: '{{n}} файлов уже находятся по правильному пути', + migrateCollisions: '{{n}} файлов невозможно переименовать автоматически (несколько треков ведут к одному пути). Они останутся нетронутыми — следующая синхронизация скачает их в нужное место.', + migratePreviewNote: 'Старый шаблон: {{tpl}}', + migrateExecuting: 'Переименование файлов…', + migrateSuccess: '{{n}} файлов успешно переименовано', + migrateFailed: '{{n}} переименований завершились ошибкой', + migrateShowErrors: 'Показать ошибки', + migrateStart: 'Начать переименование', + scanningDevice: 'Сканирование устройства…', templateHint: 'Переменные: {artist}, {album}, {title}, {track_number}, {disc_number}, {year}', cleanupButton: 'Удалить файлы вне выборки', cleanupNothingToDelete: 'Нечего удалять — устройство уже синхронизировано.', diff --git a/src/locales/ru/home.ts b/src/locales/ru/home.ts index 4427b102..99736435 100644 --- a/src/locales/ru/home.ts +++ b/src/locales/ru/home.ts @@ -5,13 +5,13 @@ export const home = { recentlyPlayed: 'Недавно проиграно', losslessAlbums: 'Lossless-альбомы', discover: 'Обзор', - discoverSongs: 'Открыть треки', + discoverSongs: 'Откройте для себя', loadMore: 'Ещё', discoverMore: 'Смотреть ещё', discoverArtists: 'Исполнители', discoverArtistsMore: 'Все исполнители', - becauseYouLike: 'Потому что вы слушали…', - becauseYouLikeFor: 'Потому что вы слушали {{artist}}', + becauseYouLike: 'Поскольку вы слушали…', + becauseYouLikeFor: 'Поскольку вы слушали {{artist}}', similarTo: 'Похоже на {{artist}}', becauseYouLikeTracks_one: '{{count}} трек', becauseYouLikeTracks_few: '{{count}} трека', @@ -19,5 +19,5 @@ export const home = { becauseYouLikeTracks_other: '{{count}} треков', mainstageEmptyTitle: 'Раздел «Для вас» пуст', mainstageEmptyBody: 'Все разделы отключены. Включите разделы снова или скройте «Для вас» из боковой панели.', - mainstageEmptyCta: 'Открыть Персонализацию', + mainstageEmptyCta: 'Открыть персонализацию', }; diff --git a/src/locales/ru/orbit.ts b/src/locales/ru/orbit.ts index 7144b461..b7285e95 100644 --- a/src/locales/ru/orbit.ts +++ b/src/locales/ru/orbit.ts @@ -40,7 +40,7 @@ export const orbit = { btnStarting: 'Запуск…', btnStart: 'Запустить Orbit', btnCopyAndStart: 'Копировать ссылку и запустить', - errNameRequired: 'Пожалуйста, дай сессии название.', + errNameRequired: 'Пожалуйста, дай название сессии.', errStartFailed: 'Не удалось запустить.', hostLabel: 'Хост: {{name}}', participantsTooltip: 'Участники', @@ -134,7 +134,7 @@ export const orbit = { settingAutoApprove: 'Автоматически принимать предложения', settingAutoApproveHint: 'Предложения гостей сразу попадают в твою очередь. Выкл.: они остаются в списке сессии для последующего просмотра.', settingAutoShuffle: 'Автоперемешивание', - settingAutoShuffleHint: 'Периодическое Fisher-Yates перемешивание предстоящей очереди. Выкл.: порядок меняется только при ручной перестановке.', + settingAutoShuffleHint: 'Периодически перемешивает предстоящую очередь. Выкл.: порядок меняется только при ручной перестановке.', settingShuffleInterval: 'Перемешивать каждые', settingShuffleIntervalHint: 'Как часто предстоящая очередь перемешивается во время сессии.', suggestBlockedMuted: 'Хост отключил тебе предложение треков на этой сессии.', diff --git a/src/locales/ru/player.ts b/src/locales/ru/player.ts index 0a687317..8e4e118a 100644 --- a/src/locales/ru/player.ts +++ b/src/locales/ru/player.ts @@ -59,8 +59,11 @@ export const player = { lyricsLoading: 'Загрузка текста…', lyricsNotFound: 'Текст не найден', lyricsNoSources: 'Источники текста не выбраны — включите их в Настройки → Текст', + lyricsSourceServer: 'Источник: Сервер', + lyricsSourceLrclib: 'Источник: LRCLIB', lyricsSourceNetease: 'Источник: Netease', lyricsSourceLyricsplus: 'Источник: YouLyPlus', + fsLyricsToggle: 'Текст в полноэкранном режиме', showDuration: 'Показать длительность', showRemainingTime: 'Показать оставшееся время', }; diff --git a/src/locales/ru/settings.ts b/src/locales/ru/settings.ts index 6706ef94..d48336eb 100644 --- a/src/locales/ru/settings.ts +++ b/src/locales/ru/settings.ts @@ -43,7 +43,7 @@ export const settings = { serverUsername: 'Логин', serverPassword: 'Пароль', customHeadersTitle: 'Пользовательские HTTP-заголовки', - customHeadersHelp: 'Для reverse-proxy (Pangolin, Cloudflare Access). Отправляются только на ваш сервер — не сторонним сервисам.', + customHeadersHelp: 'Используются для reverse-proxy (Pangolin, Cloudflare Access). Отправляются только на ваш сервер — не сторонним сервисам.', customHeadersNamePlaceholder: 'Имя заголовка', customHeadersValuePlaceholder: 'Значение заголовка', customHeadersAddRow: 'Добавить заголовок', @@ -239,7 +239,7 @@ export const settings = { hiResTitle: 'Нативное воспроизведение Hi-Res', hiResEnabled: 'Включить нативное Hi-Res воспроизведение', hiResDesc: "Воспроизводит каждый трек с его исходной частотой дискретизации вместо передискретизации всего в 44,1 кГц, переключая устройство вывода под частоту файла (88,2 кГц и выше). Включайте только если оборудование и сеть надёжно справляются с высокими частотами.", - hiResCrossfadeResampleTitle: 'Частота сведения Crossfade / AutoDJ / Gapless', + hiResCrossfadeResampleTitle: 'Частота сведения при переходах', hiResCrossfadeResampleDesc: 'Когда соседние треки имеют разную частоту дискретизации, обе стороны передискретизируются на эту частоту во время кроссфейда, AutoDJ и gapless-переходов.', hiResCrossfadeResample44: '44,1 кГц', hiResCrossfadeResample88: '88,2 кГц', @@ -277,10 +277,10 @@ export const settings = { linuxWebkitSmoothScroll: 'Плавное колесо (Linux)', linuxWebkitSmoothScrollDesc: 'Вкл — инерция. Выкл — по шагам, как в GTK.', linuxWebkitInputForceRepaint: 'Перерисовывать поля ввода при фокусе (Linux)', - linuxWebkitInputForceRepaintDesc: 'Обход проблемы WebKitGTK 2.50.x, когда текстовые поля зависают при клике. Иконки поиска кратко мигают при фокусе.', + linuxWebkitInputForceRepaintDesc: 'Исправляет зависание текстовых полей при клике. Иконки поиска могут кратко мигать при фокусе.', linuxWaylandTextRender: 'Рендеринг текста Wayland (Linux)', linuxWaylandTextRenderDesc: - 'Сглаживание в интерфейсе меняется сразу. Политику ускорения WebKit (чёткий / GPU) сохраняем и применяем при следующем запуске — переключение на лету на части сборок зависает в WebKitGTK.', + 'Сглаживание меняется сразу. Пресет ускорения (Чёткий / GPU) применяется после перезапуска приложения.', linuxWaylandTextRenderBalanced: 'Сбалансированный', linuxWaylandTextRenderSharp: 'Чёткий (меньше GPU)', linuxWaylandTextRenderGpu: 'С приоритетом GPU', @@ -417,7 +417,7 @@ export const settings = { analyticsStrategyClearError: 'Не удалось очистить анализ для удалённого сервера.', analyticsStrategyProgressEmptyAfterFailed: '100% (0/0, треки помечены как проблемные)', analyticsFailedTracksOpenTitle: 'Проблемных треков: {{count}}', - analyticsFailedTracksTitle: 'Проблемные треки анализа — {{server}}', + analyticsFailedTracksTitle: 'Проблемные треки при анализе — {{server}}', analyticsFailedTracksDesc: 'Эти треки многократно падали на decode/enrichment и исключены из фонового автоанализа.', analyticsFailedTracksLoading: 'Загрузка списка проблемных треков…', analyticsFailedTracksEmpty: 'Для этого сервера проблемных треков нет.', @@ -425,9 +425,9 @@ export const settings = { analyticsFailedTracksExport: 'Экспорт списка', analyticsFailedTracksExportSuccess: 'Список проблемных треков экспортирован ({{count}}).', analyticsFailedTracksExportError: 'Не удалось экспортировать список проблемных треков.', - analyticsFailedTracksRescan: 'Рескан проблемных треков', - analyticsFailedTracksRescanSuccess: 'Рескан запланирован для {{count}} проблемных треков.', - analyticsFailedTracksRescanError: 'Не удалось запустить рескан проблемных треков.', + analyticsFailedTracksRescan: 'Повторное сканирование проблемных треков', + analyticsFailedTracksRescanSuccess: 'Повторное сканирование запланировано для {{count}} проблемных треков.', + analyticsFailedTracksRescanError: 'Не удалось запустить повторное сканирование проблемных треков.', analyticsFailedTracksLoadError: 'Не удалось загрузить список проблемных треков.', randomMixTitle: 'Чёрный список случайного микса', luckyMixMenuTitle: 'Показывать «Мне повезёт» в меню', @@ -579,7 +579,7 @@ export const settings = { tabUsers: 'Пользователи', tabSystem: 'Система', loggingTitle: 'Логирование', - loggingModeDesc: 'Управляет подробностью логов backend в терминале.', + loggingModeDesc: 'Управляет подробностью логов бэкенда в терминале.', loggingModeOff: 'Выключено', loggingModeNormal: 'Обычное', loggingModeDebug: 'Дебаг', @@ -701,7 +701,7 @@ export const settings = { autodjOverlapCapAuto: 'Авто', autodjOverlapCapLimit: 'Лимит', autodjOverlapCapSecs: '{{n}} с', - gapless: 'Без пауз между треками', + gapless: 'Без пауз', transitionsTitle: 'Переходы между треками', transitionsDesc: 'Как соединяются идущие подряд треки. Одновременно может быть активен только один режим.', transitionOff: 'Выкл.', @@ -724,7 +724,7 @@ export const settings = { trackPreviewLocation_playlists: 'В плейлистах', trackPreviewLocation_favorites: 'В избранном', trackPreviewLocation_artist: 'В топ-треках исполнителя', - trackPreviewLocation_randomMix: 'В Random Mix', + trackPreviewLocation_randomMix: 'В случайном миксе', nextTrackBufferingTitle: 'Буферизация', infiniteQueue: 'Бесконечная очередь', infiniteQueueDesc: 'Подмешивать случайные треки, когда очередь закончится', @@ -766,12 +766,12 @@ export const settings = { themeSchedulerLightTheme: 'Светлая тема', themeSchedulerDarkTheme: 'Тёмная тема', themeSchedulerSystemRestartHint: 'Изменение светлой/тёмной настройки системы вступит в силу после перезапуска приложения.', - visualOptionsTitle: 'Визуальные Настройки', - coverArtBackground: 'Фон Обложки', + visualOptionsTitle: 'Визуальные настройки', + coverArtBackground: 'Фон обложки', coverArtBackgroundSub: 'Показывать размытую обложку как фон в заголовках альбомов и плейлистов', - playlistCoverPhoto: 'Обложка Плейлиста', + playlistCoverPhoto: 'Обложка плейлиста', playlistCoverPhotoSub: 'Показывать сетку обложек в детальном виде плейлиста', - showBitrate: 'Показывать Битрейт', + showBitrate: 'Показывать битрейт', showBitrateSub: 'Отображать битрейт аудио в списках треков', floatingPlayerBar: 'Плавающая панель плеера', floatingPlayerBarSub: 'Держать панель плеера плавающей над содержимым', diff --git a/src/locales/ru/sidebar.ts b/src/locales/ru/sidebar.ts index a7dd7a19..df284de7 100644 --- a/src/locales/ru/sidebar.ts +++ b/src/locales/ru/sidebar.ts @@ -25,7 +25,7 @@ export const sidebar = { genres: 'Жанры', tracks: 'Треки', playlists: 'Плейлисты', - smartPlaylists: 'Смарт-плейлисты', + smartPlaylists: 'Умные плейлисты', mostPlayed: 'Популярное', losslessAlbums: 'Lossless', radio: 'Онлайн-радио', diff --git a/src/locales/ru/statistics.ts b/src/locales/ru/statistics.ts index 266350d2..47a89185 100644 --- a/src/locales/ru/statistics.ts +++ b/src/locales/ru/statistics.ts @@ -84,4 +84,22 @@ export const statistics = { playerListenedMinDecimal: '{{minutes}} мин', completionFull: 'Прослушано', completionPartial: 'Частично', + exportShare: 'Поделиться', + exportTitle: 'Поделиться топ-альбомами', + exportSubtitle: 'Создайте изображение с вашими самыми прослушиваемыми альбомами.', + exportFormat: 'Формат', + exportFormatStory: 'История', + exportFormatSquare: 'Квадрат', + exportFormatTwitter: 'Twitter Card', + exportGrid: 'Сетка', + exportGridLabel: '{{n}}×{{n}}', + exportPreview: 'Предпросмотр', + exportGenerate: 'Создать', + exportSave: 'Сохранить изображение…', + exportCancel: 'Отмена', + exportFooterLabel: 'Топ альбомов', + exportNotEnough: 'Нужно не менее {{count}} альбомов в библиотеке для сетки {{n}}×{{n}}.', + exportSaving: 'Сохранение…', + exportSaved: 'Сохранено.', + exportSaveFailed: 'Не удалось сохранить изображение.', }; diff --git a/src/locales/ru/tracks.ts b/src/locales/ru/tracks.ts index d198648b..8bda3a9c 100644 --- a/src/locales/ru/tracks.ts +++ b/src/locales/ru/tracks.ts @@ -1,7 +1,7 @@ export const tracks = { title: 'Треки', - subtitle: 'Листать. Искать. Открывать.', - heroEyebrow: 'Трек момента', + subtitle: 'Листай. Ищи. Открывай.', + heroEyebrow: 'Рекомендуем', heroReroll: 'Выбрать другой', playSong: 'Воспроизвести', enqueueSong: 'В очередь',