Files
mcma-webui/src/i18n/locales/ru.ts
T
Цвылев Александр Вадимович e45ef785cf feat(player): stream at the chosen quality + per-track optimize (§6.6)
The Playback quality setting now drives streaming: getStreamUrl appends
?quality= and useAudioPlayer feeds the server-synced streamQuality, so the
backend transcodes on demand (miss → original). Adds an optimizeTrack mutation
and an Optimize action in the metadata editor to warm a track's rendition.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 21:22:59 +03:00

671 lines
27 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Translations } from './en';
const ru: Translations = {
nav: {
library: 'Библиотека',
search: 'Поиск и загрузка',
downloads: 'Загрузки',
upload: 'Загрузить',
storage: 'Хранилище',
playlists: 'Плейлисты',
newPlaylist: 'Новый плейлист',
admin: 'Администрирование',
settings: 'Настройки',
administration: 'Администрирование',
},
conn: {
connected: 'Подключено',
connecting: 'Подключение…',
disconnected: 'Нет связи',
error: 'Недоступно',
manage: 'Соединение — управление экземплярами',
cached: 'Показаны последние данные',
},
user: {
online: 'онлайн',
offline: 'офлайн',
signOut: 'Выйти',
},
connect: {
domains: {
title: 'Сохранённые серверы',
addPlaceholder: 'https://your-server.example.com',
addButton: 'Добавить сервер',
selected: 'Выбран',
use: 'Выбрать',
forgetTitle: 'Удалить этот сервер',
},
removeDialog: {
title: 'Удалить локальные данные?',
description:
'Сервер «{{name}}» будет удалён из сохранённых, а его данные на этом устройстве — очищены.',
cancel: 'Отмена',
logout: 'Просто выйти',
removeAndLogout: 'Удалить данные и выйти',
},
login: {
title: 'Вход в {{name}}',
registerTitle: 'Регистрация на {{name}}',
username: 'Имя пользователя',
password: 'Пароль',
passwordHint: 'Не менее 8 символов.',
submit: 'Войти',
submitting: 'Вход…',
registerSubmit: 'Зарегистрироваться',
registering: 'Регистрация…',
noAccount: 'Нет аккаунта?',
registerLink: 'Зарегистрироваться',
haveAccount: 'Уже есть аккаунт?',
signInLink: 'Войти',
},
errors: {
unreachable:
'Не удаётся подключиться к серверу. Проверьте URL и доступность.',
badCredentials: 'Неверное имя пользователя или пароль.',
generic: 'Не удалось войти. Попробуйте ещё раз.',
usernameTaken: 'Это имя пользователя уже занято.',
passwordTooShort: 'Пароль должен содержать не менее 8 символов.',
registrationDisabled: 'Регистрация на этом сервере отключена.',
registerFailed: 'Не удалось создать аккаунт. Попробуйте ещё раз.',
},
},
library: {
title: 'Библиотека',
searchPlaceholder: 'Поиск в библиотеке…',
tabs: {
tracks: 'Треки',
albums: 'Альбомы',
artists: 'Исполнители',
},
playAll: '▶ Воспроизвести все ({{count}})',
empty: {
tracks: {
title: 'Нет треков',
description: 'Библиотека пуста. Начните с загрузки музыки.',
},
albums: {
title: 'Нет альбомов',
description: 'В библиотеке нет альбомов.',
},
artists: {
title: 'Нет исполнителей',
description: 'В библиотеке нет исполнителей.',
},
},
albumCard: {
tracks: '{{count}} треков',
tracksDuration: '{{count}} треков · {{duration}}',
},
artistRow: {
meta: '{{albumCount}} альб. · {{trackCount}} треков',
},
offline: {
banner:
'Нет связи с сервером — показана локально доступная библиотека. Она может быть неполной и доступна только для чтения, пока сервер недоступен.',
emptyTitle: 'Офлайн ничего нет',
emptyDesc:
'На этом устройстве ещё нет кэша библиотеки. Подключитесь к серверу хотя бы раз, чтобы просматривать офлайн.',
},
},
album: {
type: 'Альбом',
play: '▶ Слушать',
error: 'Не удалось загрузить альбом',
tracksError: 'Не удалось загрузить треки',
empty: {
title: 'Нет треков',
description: 'В этом альбоме нет треков.',
},
offline: {
title: 'Альбом недоступен офлайн',
description: 'Нет связи, а этот альбом не сохранён на устройстве.',
},
},
artist: {
type: 'Исполнитель',
play: '▶ Слушать всё',
error: 'Не удалось загрузить исполнителя',
meta: '{{albumCount}} альбомов · {{trackCount}} треков',
albums: 'Альбомы',
tracks: 'Треки',
noAlbums: 'Пока нет альбомов.',
empty: {
title: 'Нет треков',
description: 'У этого исполнителя нет треков.',
},
offline: {
title: 'Исполнитель недоступен офлайн',
description: 'Нет связи, а этот исполнитель не сохранён на устройстве.',
},
},
playlist: {
type: 'Плейлист',
play: '▶ Слушать',
error: 'Не удалось загрузить плейлист',
tracksError: 'Не удалось загрузить треки',
empty: {
title: 'Плейлист пуст',
description: 'В этом плейлисте пока нет треков.',
},
menu: {
rename: 'Переименовать',
delete: 'Удалить',
},
},
player: {
nothingPlaying: 'Ничего не играет',
previous: 'Назад',
next: 'Вперёд',
pause: 'Пауза',
play: 'Воспроизвести',
streaming: 'Стриминг',
local: 'Локально',
queue: 'Очередь',
mute: 'Выключить звук',
unmute: 'Включить звук',
},
nowPlaying: {
expand: 'Сейчас играет',
collapse: 'Свернуть',
artwork: 'Обложка',
lyrics: 'Текст',
},
lyrics: {
none: 'Текст для этого трека не найден.',
refetch: 'Искать снова',
},
queue: {
title: 'Очередь воспроизведения',
shuffle: 'Перемешать очередь',
loop: 'Повторять текущий трек',
clear: 'Очистить очередь',
close: 'Закрыть',
from: 'Из: {{source}}',
radio: 'Радио · {{source}}',
nextUp: 'Далее',
empty: 'Очередь пуста',
radioActive: 'Радио активно',
mixing: '∞ микс',
familiar: 'Знакомое',
new: 'Новое',
loadingMore: 'Загрузка радио…',
doubleClickPlay: 'Двойной клик для воспроизведения',
removeFromQueue: 'Убрать из очереди',
menu: {
options: 'Параметры трека',
playNow: 'Воспроизвести сейчас',
moveNext: 'Сделать следующим',
info: 'Информация о треке',
remove: 'Убрать из очереди',
},
},
track: {
menu: {
options: 'Действия с треком',
playNow: 'Играть сейчас',
playNext: 'Следующим',
addToQueue: 'Добавить в очередь',
info: 'Информация о треке',
addToPlaylist: 'Добавить в плейлист…',
removeFromPlaylist: 'Убрать из плейлиста',
editMetadata: 'Редактировать метаданные',
download: 'Скачать',
delete: 'Удалить',
},
},
addToPlaylist: {
title: 'Добавить в плейлист',
empty: 'У вас пока нет плейлистов.',
newPlaylist: 'Новый плейлист',
newPlaceholder: 'Название плейлиста',
create: 'Создать и добавить',
added: 'Добавлено в «{{name}}».',
error: 'Не удалось добавить трек в этот плейлист. Попробуйте ещё раз.',
createError: 'Не удалось создать плейлист. Попробуйте ещё раз.',
},
playlists: {
newDialog: {
title: 'Новый плейлист',
placeholder: 'Название плейлиста',
cancel: 'Отмена',
create: 'Создать',
},
empty: {
title: 'Пока нет плейлистов',
description: 'Создайте первый плейлист, чтобы начать организовывать треки.',
},
trackCount: '{{count}} треков',
menu: {
options: 'Действия с плейлистом',
rename: 'Переименовать',
delete: 'Удалить',
},
renameDialog: {
title: 'Переименовать плейлист',
save: 'Сохранить',
},
deleteDialog: {
title: 'Удалить плейлист?',
description: 'Плейлист «{{name}}» будет удалён навсегда. Треки останутся в библиотеке.',
cancel: 'Отмена',
confirm: 'Удалить',
},
},
trackInfo: {
title: 'О треке',
open: 'Информация о треке',
close: 'Закрыть',
notFound: 'Трек не найден',
play: 'Играть',
addToQueue: 'В очередь',
editMetadata: 'Метаданные',
liked: 'В избранном',
trackOf: '№ {{n}} из {{total}}',
kbps: '{{n}} кбит/с',
sections: {
status: 'Статус',
general: 'Основное',
file: 'Файл',
identifiers: 'Идентификаторы',
},
fields: {
artist: 'Исполнитель',
album: 'Альбом',
trackNumber: 'Трек',
disc: 'Диск',
year: 'Год',
genre: 'Жанр',
duration: 'Длительность',
format: 'Формат',
bitrate: 'Битрейт',
size: 'Размер',
source: 'Источник',
added: 'Добавлен',
enriched: 'Обогащён',
trackId: 'ID трека',
albumId: 'ID альбома',
artistId: 'ID исполнителя',
},
},
common: {
error: 'Что-то пошло не так',
retry: 'Повторить',
comingSoon: 'Скоро',
back: 'Назад',
offlineBanner:
'Нет связи с сервером — показаны локально доступные данные, только для чтения.',
},
storage: {
subtitle: 'Всё, что хранит этот инстанс',
maintenance: {
subtitle:
'Найдите дубликаты, треки с ошибкой обогащения и файлы без метаданных — и уберите осиротевшие записи.',
tabs: {
duplicates: 'Дубликаты',
broken: 'С ошибкой',
missing: 'Без метаданных',
},
cleanup: {
title: 'Очистить осиротевшие файлы',
description:
'Сверить каталог и удалить записи, у которых пропал файл на диске. Выполняется в фоне.',
button: 'Запустить очистку',
enqueued: 'Очистка поставлена в очередь',
},
duplicates: {
empty: 'Дубликатов не найдено.',
groupTitle: '{{count}} треков с одним отпечатком',
},
broken: {
empty: 'Треков с ошибкой нет.',
},
missing: {
empty: 'У всех треков есть метаданные.',
},
pagination: '{{from}}–{{to}} из {{total}}',
prev: 'Назад',
next: 'Вперёд',
editBatch: 'Редактировать массово',
},
device: 'На этом устройстве',
server: 'На сервере',
audioCache: 'Кэш аудио',
audioCacheUsage: 'Занято {{used}} из {{max}}',
cachedTracks: '{{n}} треков сохранено офлайн',
audioCacheUnavailable:
'Офлайн-кэш аудио недоступен (service worker не активен).',
offlineLibrary: 'Офлайн-библиотека',
offlineLibraryMeta:
'{{tracks}} треков · {{albums}} альбомов · {{artists}} исполнителей доступно офлайн',
serverUnreachable: 'Сервер недоступен — показано только это устройство.',
emptyTitle: 'Пока ничего не сохранено',
emptyDesc:
'Загрузите немного музыки — и здесь появится статистика вашей библиотеки.',
disk: 'Диск',
diskUsage: 'Занято {{used}} из {{total}}',
diskFree: 'Свободно {{free}}',
diskLibraryShare: 'Библиотека занимает {{percent}}% всего диска',
diskUnknown: 'Объектное хранилище — фиксированного диска нет',
footprint: 'Объём библиотеки',
tracks: 'Треки',
artists: 'Исполнители',
albums: 'Альбомы',
playtime: 'Общая длительность',
avgTrackSize: 'Средний размер трека',
largestTrack: 'Самый большой трек',
formats: 'Форматы',
sources: 'Откуда взято',
metadataHealth: 'Состояние метаданных',
topGenres: 'Топ жанров',
noGenres: 'Жанры пока не указаны',
funFacts: 'Интересные факты',
factPlaytime:
'Нажмите play и уходите — библиотека играет {{duration}} без остановки.',
factFootprint: '{{size}} музыки в {{tracks}} треках.',
factGenre: 'Чаще всего встречается жанр {{genre}} ({{count}} треков).',
factAvg: 'Средний трек весит {{size}}.',
factSince: 'Коллекция собирается с {{date}}.',
status: {
enriched: 'Обогащено',
manual: 'Вручную',
pending: 'В ожидании',
failed: 'Ошибка',
},
},
pages: {
admin: 'Администрирование',
settings: 'Настройки',
downloads: 'Загрузки',
search: 'Поиск и загрузка',
storage: 'Хранилище',
login: 'Вход',
artist: 'Артист',
playlists: 'Плейлисты',
upload: 'Загрузка файлов',
metadata: 'Редактирование метаданных',
metadataBatch: 'Редактирование метаданных (массово)',
storageMaintenance: 'Обслуживание хранилища',
queue: 'Очередь воспроизведения',
},
settings: {
language: 'Язык',
theme: 'Тема',
themeDark: 'Тёмная',
themeLight: 'Светлая',
tabs: {
profile: 'Профиль',
playback: 'Воспроизведение',
scrobbling: 'Скробблинг',
instance: 'Сервер',
},
common: {
save: 'Сохранить',
saving: 'Сохранение…',
},
instance: {
current: 'Текущий инстанс',
none: 'Инстанс не выбран.',
manage: 'Управление инстансами',
signOut: 'Выйти',
},
playback: {
streamQuality: 'Качество потока',
streamQualityHint:
'Качество звука при стриминге с сервера. «Оригинал» отдаёт файл как есть.',
quality: {
original: 'Оригинал',
high: 'Высокое',
medium: 'Среднее',
low: 'Низкое',
},
},
scrobbling: {
description: 'Отправляйте свои прослушивания в Last.fm или ListenBrainz.',
configured: 'Подключено',
notConfigured: 'Не подключено',
enabled: 'Скробблинг',
on: 'Вкл',
off: 'Выкл',
provider: 'Сервис',
lastfm: 'Last.fm',
listenbrainz: 'ListenBrainz',
username: 'Имя пользователя',
usernamePlaceholder: 'ваш логин',
sessionKey: 'Ключ сессии / токен',
sessionKeyPlaceholder: 'вставьте токен',
sessionKeyKeep: 'оставьте пустым, чтобы сохранить текущий токен',
sessionKeyHint:
'Хранится на сервере в зашифрованном виде и больше не показывается.',
saved: 'Настройки скробблинга сохранены.',
error: 'Не удалось сохранить. Попробуйте ещё раз.',
},
},
admin: {
userDetail: 'Пользователь',
tabs: {
users: 'Пользователи',
sources: 'Источники',
instance: 'Сервер',
},
users: {
add: 'Добавить',
empty: 'Пользователей пока нет.',
you: '(вы)',
added: 'Добавлен {{date}}',
role: {
admin: 'Админ',
user: 'Пользователь',
},
status: {
blocked: 'Заблокирован',
},
newDialog: {
title: 'Новый пользователь',
username: 'Логин',
password: 'Пароль',
create: 'Создать',
cancel: 'Отмена',
error: 'Не удалось создать пользователя — возможно, логин занят.',
},
menu: {
options: 'Действия',
makeAdmin: 'Сделать админом',
makeUser: 'Снять админа',
resetPassword: 'Сбросить пароль',
activate: 'Разблокировать',
deactivate: 'Заблокировать',
},
resetDialog: {
title: 'Сброс пароля — {{name}}',
newPassword: 'Новый пароль',
submit: 'Задать пароль',
cancel: 'Отмена',
done: 'Пароль обновлён.',
},
deactivateDialog: {
title: 'Заблокировать пользователя?',
description:
'{{name}} будет разлогинен и не сможет войти. Позже можно разблокировать.',
confirm: 'Заблокировать',
cancel: 'Отмена',
},
},
instance: {
status: {
ok: 'Работает',
down: 'Недоступен',
skipped: 'Не настроен',
},
services: {
title: 'Сервисы',
database: 'База данных',
redis: 'Redis',
ml: 'ML-сервис',
},
sources: {
title: 'Источники',
envNote: 'Источники настраиваются через переменные окружения.',
empty: 'Источники не настроены.',
available: 'Доступен',
unavailable: 'Недоступен',
},
reindex: {
title: 'Переиндексация библиотеки',
description:
'Пересканировать все индексируемые источники. Выполняется в фоне, идемпотентно.',
button: 'Переиндексировать',
enqueued: 'Переиндексация запущена ({{count}} источников).',
noSource: 'Индексируемые источники не настроены.',
},
},
},
notFound: {
title: 'Страница не найдена',
description: 'Этого экрана пока нет.',
backToLibrary: 'Вернуться в библиотеку',
},
upload: {
title: 'Загрузка файлов',
dropzone: {
title: 'Перетащите аудиофайлы сюда',
hint: 'или нажмите, чтобы выбрать файлы — по одному или сразу несколько',
button: 'Выбрать файлы',
},
queueTitle: 'Загрузки ({{completed}}/{{total}})',
clearCompleted: 'Убрать завершённые',
retry: 'Повторить',
editMetadata: 'Изменить метаданные',
recent: {
title: 'Недавно загруженные',
empty: 'Пока ничего не загружено.',
},
metadataPending:
'Загруженные треки появляются как «Unknown Artist» с метаданными в ожидании — дозаполните их позже.',
unknownArtist: 'Unknown Artist · метаданные в ожидании',
status: {
queued: 'В очереди',
uploading: 'Загрузка',
done: 'Загружено',
duplicate: 'Уже в библиотеке',
error: 'Ошибка',
},
},
discover: {
title: 'Поиск и скачивание',
subtitle:
'Находите музыку в подключённых источниках и добавляйте в библиотеку.',
searchPlaceholder: 'Найти трек, исполнителя или альбом…',
searchButton: 'Найти',
allSources: 'Все источники',
noSources:
'Источники скачивания не настроены. Включите источник (например, YouTube Music) на сервере, чтобы искать и скачивать.',
startTitle: 'Начните с поиска',
startDesc: 'Здесь появятся результаты из подключённых источников.',
emptyTitle: 'Ничего не найдено',
emptyDesc: 'Попробуйте другой запрос или другой источник.',
searchError: 'Не удалось выполнить поиск. Попробуйте ещё раз.',
download: 'Скачать',
retryDownload: 'Повторить',
queued: 'В очереди',
inLibrary: 'В библиотеке',
viewDownloads: 'К загрузкам',
},
downloads: {
title: 'Загрузки',
subtitle: 'Активные, завершённые и неуспешные скачивания.',
activeCount: 'Активных: {{count}}',
sectionActive: 'В процессе',
sectionHistory: 'История',
emptyTitle: 'Пока нет загрузок',
emptyDesc: 'Найдите музыку в разделе «Поиск и скачивание».',
loadError: 'Не удалось загрузить список.',
failedBanner:
'Неуспешных скачиваний: {{count}}. Повторите их или проверьте источник на сервере.',
attempt: 'Попытка {{count}}',
open: 'Открыть',
retry: 'Повторить',
cancel: 'Отменить',
status: {
queued: 'В очереди',
downloading: 'Скачивание',
enriching: 'Обработка',
done: 'Готово',
failed: 'Ошибка',
},
},
metadata: {
status: {
pending: 'Обработка…',
enriched: 'Готово',
failed: 'Нет совпадения',
manual: 'Вручную',
},
statusHint: {
pending: 'Определяем метаданные…',
enriched: 'Метаданные определены',
failed: 'Не удалось определить метаданные',
manual: 'Изменено вручную — не обновляется автоматически',
},
},
metadataBatch: {
selectedCount: 'Выбрано {{selected}} из {{total}}',
status: {
pending: 'Без метаданных',
failed: 'С ошибкой',
},
result: 'Обновлено {{ok}}, ошибок {{fail}}',
reEnrich: {
title: 'Повторное авто-обогащение',
hint: 'Поставить в очередь идентификацию AcoustID/MusicBrainz для выбранных треков.',
button: 'Переобогатить выбранные',
},
common: {
title: 'Применить общие поля',
hint: 'Заполненные поля запишутся всем выбранным трекам; пустые останутся без изменений.',
apply: 'Применить к выбранным',
},
empty: 'В этой категории пусто.',
},
metadataEditor: {
optimize: {
title: 'Оптимизировать формат',
hint: 'Перекодировать в компактный Opus + HLS в фоне.',
button: 'Оптимизировать',
enqueued: 'Оптимизация поставлена в очередь — выполнится в фоне.',
},
error: 'Не удалось загрузить трек',
saved: 'Метаданные сохранены.',
saveError: 'Не удалось сохранить метаданные.',
save: 'Сохранить',
fields: {
title: 'Название',
artist: 'Исполнитель',
album: 'Альбом',
year: 'Год',
genre: 'Жанр',
trackNumber: 'Номер трека',
},
autoEnrich: {
title: 'Поиск по AcoustID',
hint: 'Определить трек по аудио-отпечатку.',
findMatches: 'Найти совпадения',
reEnrich: 'Повторить обогащение',
enqueued: 'Обогащение запущено — обновите через момент.',
error: 'Не удалось найти совпадения.',
noMatches: 'Совпадений не найдено.',
},
matches: {
use: 'Использовать',
unknownTitle: 'Неизвестное название',
},
diff: {
title: 'Применить это совпадение?',
noChanges: 'Нет изменений относительно текущих значений.',
cancel: 'Отмена',
apply: 'Применить',
},
},
};
export default ru;