231887c3b7
Adds DownloadJob/ExternalSearchResult/SourceInfo contract types + mappers, the downloads + search RTKQ endpoints, and the SearchDownloadPage (search external sources, per-result download states) and DownloadsManagerPage (active/history, progress, retry/cancel, poll-while-active). en/ru i18n. Snapshot also bundles in-progress queue/metadata-editor/storage UI work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
451 lines
18 KiB
TypeScript
451 lines
18 KiB
TypeScript
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: 'В этом плейлисте пока нет треков.',
|
||
},
|
||
},
|
||
player: {
|
||
nothingPlaying: 'Ничего не играет',
|
||
previous: 'Назад',
|
||
next: 'Вперёд',
|
||
pause: 'Пауза',
|
||
play: 'Воспроизвести',
|
||
streaming: 'Стриминг',
|
||
local: 'Локально',
|
||
queue: 'Очередь',
|
||
mute: 'Выключить звук',
|
||
unmute: 'Включить звук',
|
||
},
|
||
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: 'Добавить в плейлист…',
|
||
editMetadata: 'Редактировать метаданные',
|
||
download: 'Скачать',
|
||
delete: 'Удалить',
|
||
},
|
||
},
|
||
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: 'Всё, что хранит этот инстанс',
|
||
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: 'Сервер',
|
||
},
|
||
},
|
||
admin: {
|
||
userDetail: 'Пользователь',
|
||
tabs: {
|
||
users: 'Пользователи',
|
||
sources: 'Источники',
|
||
instance: 'Сервер',
|
||
},
|
||
},
|
||
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: 'Изменено вручную — не обновляется автоматически',
|
||
},
|
||
},
|
||
metadataEditor: {
|
||
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;
|