Files
TeleWave/frontend/src/shared/lib/locales/ru.ts
T
Leonid Pershin bee655ec1c
ci / build-backend (push) Successful in 1m23s
ci / build-frontend (push) Successful in 39s
ci / tests (push) Successful in 1m22s
ci / sonar (push) Successful in 3m43s
Refactor media preview functionality and update localization strings
Moved the mediaPreviewUrl function to the media API for better organization and clarity. Updated the MediaPanel component to include a preview feature for media assets, allowing users to view processed clips. Enhanced the UI to include a dialog for media previews and added corresponding localization strings in both English and Russian for the new preview functionality.
2026-07-30 23:24:35 +03:00

1043 lines
62 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.
/** Словарь локали «ru». Плоская правка: ключи обеих локалей обязаны совпадать. */
export const ru = {
appName: 'TeleWave',
nav: {
dashboard: 'Эфир',
admin: 'Админка',
settings: 'Настройки',
login: 'Войти',
register: 'Регистрация',
logout: 'Выйти',
},
theme: { light: 'Светлая', dark: 'Тёмная', system: 'Системная' },
lang: { ru: 'RU', en: 'EN' },
common: {
save: 'Сохранить',
cancel: 'Отмена',
retry: 'Повторить',
close: 'Закрыть',
delete: 'Удалить',
create: 'Создать',
loading: 'Загрузка…',
error: 'Что-то пошло не так',
search: 'Поиск',
actions: 'Действия',
prevPage: 'Предыдущая страница',
nextPage: 'Следующая страница',
yes: 'Да',
no: 'Нет',
},
home: {
title: 'TELEWAVE',
subtitle: 'ЭФИРНАЯ СЕТКА КАНАЛОВ',
tagline: 'Твои каналы. Твой эфир. В любое время.',
cta: 'Войти в эфир',
ctaRegister: 'Создать аккаунт',
},
auth: {
userName: 'Имя пользователя',
password: 'Пароль',
loginTitle: 'Вход в эфир',
loginSubtitle: 'Введите учётные данные для доступа к сетке каналов',
registerTitle: 'Новый зритель',
registerSubtitle: 'Создайте аккаунт, чтобы настроить свою сетку каналов',
submitLogin: 'Войти',
submitRegister: 'Зарегистрироваться',
noAccount: 'Нет аккаунта?',
haveAccount: 'Уже есть аккаунт?',
registrationClosed: 'Регистрация закрыта',
registrationClosedHint:
'Открытая регистрация отключена. Учётную запись может завести администратор.',
invalidCredentials: 'Неверное имя пользователя или пароль',
userNameTaken: 'Это имя пользователя уже занято',
blocked: 'Аккаунт заблокирован администратором',
genericError: 'Не удалось выполнить вход. Попробуйте ещё раз',
},
air: {
now: 'Сейчас',
next: 'Далее',
ad: 'Реклама',
bumper: 'Заставка',
fallback: 'Аварийный запас',
signOff: 'Конец вещания',
episode: 'Серия',
collectionPart: 'часть {{part}} из {{total}}',
live: 'В эфире',
volume: 'Громкость',
noChannels: 'Пока нет доступных каналов. Загляните позже.',
offline: 'Канал сейчас не в эфире',
offlineHint: 'Нет расписания или контента. Загляните позже.',
numbersHint: '↑ / ↓ — переключение каналов по номерам',
iptv: 'Плейлист IPTV',
iptvHint:
'M3U со всеми каналами и программой передач — для VLC, TiviMate и подобных. Доступ к эфиру зашит в сам файл: делиться им нельзя.',
iptvDone: 'Плейлист скачан. Файл открывает доступ к эфиру — не передавайте его никому.',
iptvFailed: 'Не удалось собрать плейлист',
retry: 'Повторить',
},
settings: {
title: 'Настройки аккаунта',
changeUserName: 'Смена имени пользователя',
newUserName: 'Новое имя пользователя',
changePassword: 'Смена пароля',
currentPassword: 'Текущий пароль',
newPassword: 'Новый пароль',
dangerZone: 'Опасная зона',
deleteAccount: 'Удалить аккаунт',
deleteAccountConfirm: 'Аккаунт и все данные будут удалены безвозвратно. Продолжить?',
saved: 'Сохранено',
},
admin: {
groups: {
title: 'Группы',
createTitle: 'Создать группу',
hint: 'Группа — что может попасть в эфир. На неё ссылается слот сетки, из неё стратегия выбирает элемент.',
name: 'Название',
description: 'Описание',
items: 'Позиций',
units: 'Единиц',
duration: 'Объём',
hoursShort: 'ч',
minutesShort: 'мин',
hasFilter: 'с правилом',
composition: 'Состав',
empty: 'Группа пуста',
orderHint: 'Порядок задаётся перетаскиванием — по нему идут последовательные стратегии.',
showAdvanced: 'Дополнительно',
hideAdvanced: 'Скрыть',
find: 'Подобрать',
addFound: 'Добавить найденное',
found: 'Найдено: {{total}}, новых: {{fresh}}',
added: 'Добавлено позиций: {{count}}',
alreadyIn: 'уже в группе',
mode: {
label: 'Состав',
Static: 'Явный список',
Dynamic: 'По правилу',
StaticHint: 'Позиции добавляются вручную; правило только помогает их найти.',
DynamicHint:
'Состав считается правилом при каждом обращении — новое из библиотеки попадает в эфир само.',
},
pinned: 'закреплено',
byRule: 'по правилу',
excluded: 'Исключено',
excludeHint: 'Исключить из состава — правило больше не будет его подставлять',
restore: 'Вернуть',
dynamicOrderHint:
'Порядок: сначала закреплённое (перетаскиванием), затем найденное правилом по алфавиту.',
previewComposition: 'При этом правиле в составе будет: {{count}}',
pendingFound: 'Под правило группы подходит нового: {{count}} —',
pendingAdd: 'Добавить все ({{count}})',
elementKinds: { Show: 'Шоу', Collection: 'Коллекция' },
suggestions: {
title: 'Предложения',
hint: 'Собрано по текущей библиотеке. Кнопка создаёт группу с этим составом и сохраняет правило набора — новое из библиотеки потом добавится в один клик.',
stats: '{{shows}} шоу · {{units}} ед.',
exists: 'уже есть',
created: 'Группа создана',
allCreated: 'Все предложения уже созданы.',
showExisting: 'Показать уже созданные ({{count}})',
hideExisting: 'Скрыть уже созданные',
kinds: {
Genre: 'жанр',
ShowKind: 'тип',
Audience: 'рейтинг',
BigSeries: 'крупный сериал',
},
},
filter: {
title: 'Правило набора',
hint: 'В явном списке правило только ищет кандидатов; в группе «по правилу» оно и есть состав.',
elementKinds: 'Что искать',
showKinds: 'Тип шоу',
showKindsHint:
'Не отмечено — кино и сериалы. Ролики живут отдельной библиотекой и попадают в набор, только если отметить их явно.',
genres: 'Жанры',
genresHint: 'Любой из отмеченных.',
maxAudience: 'Возраст не строже',
minAudience: 'Возраст не мягче',
minAudienceHint:
'Для ночных слотов. В отличие от верхней границы, шоу без рейтинга сюда не попадают — иначе в «взрослое» просочились бы непроставленные мультики.',
audienceHint:
'Рейтинги упорядочены по строгости: G → PG → PG-13 → R → NC-17. Шоу без рейтинга остаются в выборке.',
year: 'Год',
unitMinutes: 'Длительность единицы, мин',
unitMinutesHint:
'Средняя длина серии или фильма. Позиции без готовых ассетов фильтр не отбрасывает.',
from: 'от',
to: 'до',
any: 'любой',
},
},
collections: {
posters: 'Проставить постеры',
postersHint:
'Берёт обложку первой части у коллекций, у которых постера ещё нет. Выбранный руками не трогает.',
postersDone: 'Постеры проставлены: {{count}}',
title: 'Коллекции',
createTitle: 'Создать коллекцию',
hint: 'Франшиза — упорядоченный набор фильмов, который играется как одно целое.',
name: 'Название',
description: 'Описание',
parts: 'Части',
units: 'Единиц',
addShow: 'Добавить шоу',
pickShow: 'Выберите шоу',
empty: 'Коллекция пуста',
orderHint: 'Порядок частей задаётся перетаскиванием — в нём они и пойдут в эфир.',
suggestions: {
title: 'Предложения франшиз',
hint: 'Собрано из полнометражек, не входящих ни в одну коллекцию: что собрать в новую франшизу и чем дополнить уже собранную. Проверьте состав — по названиям догадка иногда промахивается.',
parts: '{{count}} частей',
exists: 'уже есть',
additionBadge: 'дополнение',
add: 'Добавить',
added: 'Добавлено в коллекцию: {{count}}',
created: 'Коллекция создана',
allCreated: 'Все предложения уже созданы.',
showExisting: 'Показать уже созданные ({{count}})',
hideExisting: 'Скрыть уже созданные',
sources: { Metadata: 'из метаданных', NameGuess: 'по названиям' },
},
},
interstitials: {
title: 'Ролики',
hint: 'Реклама, промо и джинглы. Перетащите ролики в сборку блока или в группу справа — блок сохранится коллекцией и пойдёт в эфир целиком.',
upload: 'Загрузить ролики',
name: 'Название',
duration: 'Длительность',
empty: 'Роликов пока нет',
noAsset: 'Без файла',
blocks: 'Блоки',
noBlocks: 'Блоков пока нет',
clipsCount: '{{count}} рол.',
blockBuilder: 'Сборка блока',
blockName: 'Название блока',
blockTotal: 'Длительность блока',
dropHint: 'Перетащите сюда ролики',
groups: 'Группы роликов',
pickGroup: 'Выберите группу',
pickGroupFirst: 'Сначала выберите группу',
dropToGroup: 'Перетащите сюда ролик или блок',
openGroup: 'Открыть группу',
newGroupName: 'Название группы',
createGroupTitle: 'Создать группу роликов',
},
genres: {
title: 'Жанры',
hint: 'Справочник жанров: по нему собираются группы контента, в него сводятся жанры из метаданных.',
name: 'Название',
slug: 'Ключ',
slugHint: 'Латиницей, без пробелов — по нему жанр опознаётся при обновлении справочника.',
aliases: 'Варианты написания',
aliasesHint:
'Через запятую. Так жанры провайдеров сводятся к вашему: tmdb:28, action, боевик.',
order: 'Порядок',
usage: 'Шоу',
system: 'Системный',
create: 'Новый жанр',
edit: 'Изменить жанр',
},
roles: {
title: 'Роли',
name: 'Название',
system: 'Системная',
create: 'Новая роль',
rename: 'Переименовать',
},
users: {
title: 'Пользователи',
userName: 'Имя пользователя',
role: 'Роль',
status: 'Статус',
createdAt: 'Регистрация',
blocked: 'Заблокирован',
active: 'Активен',
block: 'Заблокировать',
unblock: 'Разблокировать',
resetPassword: 'Пароль',
resetPasswordFor: 'Сменить пароль: {{name}}',
newPassword: 'Новый пароль',
passwordReset: 'Пароль изменён',
filterAll: 'Все роли',
createTitle: 'Создать пользователя',
password: 'Пароль',
passwordHint: 'Минимум 8 символов, хотя бы одна цифра и заглавная буква.',
create: 'Создать',
created: 'Пользователь создан',
},
movies: {
title: 'Разбор фильмов',
hint: 'Имя файла разбирается на название и год, по ним ищется фильм в источнике метаданных. Каждый файл становится отдельным шоу; строки, где совпадение неточное, поправьте руками.',
sources: { manual: 'Из папки manual', disk: 'С моего диска' },
pickFiles: 'Выбрать файлы',
scan: 'Разобрать ({{count}})',
scanning: 'Ищем в источнике…',
nothingYet: 'Выберите источник и нажмите «Разобрать».',
noProvider: 'Источник метаданных не настроен — без него искать нечем.',
file: 'Файл',
parsed: 'Название и год',
candidate: 'Кандидат',
toggleAll: 'Отметить все / снять все',
year: 'Год',
research: 'Искать заново',
empty: 'ничего не нашлось',
noCandidate: 'без метаданных',
import: 'Завести ({{count}})',
imported: 'Заведено фильмов: {{created}}, с метаданными: {{enriched}}',
uploadStarted: 'Файлы пошли на загрузку — фильмы заведутся по мере готовности',
collectionsHint:
'Франшизы после импорта соберутся в коллекции — предложения ждут на вкладке «Коллекции».',
statuses: {
Confident: 'совпадение точное',
Uncertain: 'проверьте',
NotFound: 'не найдено — впишите название',
AlreadyInLibrary: 'уже в библиотеке',
},
},
media: {
title: 'Медиа',
upload: 'Загрузить',
preview: 'Просмотр',
manualButton: 'Из папки manual',
importButton: 'Импорт',
importTitle: 'Импорт медиа',
importHint:
'Три способа пополнить библиотеку: серии из папки manual в шоу, фильмы пачкой и загрузка выбранных файлов в шоу.',
importTabs: { movies: 'Фильмы', manual: 'Серии из manual', toShow: 'Файлы в шоу' },
moviesButton: 'Фильмы',
manualTitle: 'Ручной разбор папки manual',
manualHint:
'Файлы из manual/ не разбираются сканером — выберите нужные и укажите шоу. Импортированные файлы уходят из папки, как и из inbox.',
manualSelectAll: 'Выбрать все',
manualSelected: 'Выбрано: {{count}}',
manualEmpty: 'В папке manual пусто',
regexPickHint: 'Кликните число в имени файла — по нему соберётся правило для всех файлов:',
regexPickTitle: 'Это номер серии',
regexPresets: 'Готовые:',
regexPresetNames: {
seriesWord: 'Серия N',
episodeWord: 'Эпизод N',
seasonEpisode: 'SxxEyy',
productionCode: '2ACV01',
afterDash: 'после тире',
firstNumber: 'первое число',
},
regexClear: 'сбросить',
manualAlready: 'уже в библиотеке',
manualRoot: 'корень manual/',
manualRecognized: 'Распознано: {{count}} из {{total}}',
manualCleanupHint:
'Файлы уйдут из папки, спутники (субтитры, nfo) и опустевший каталог будут удалены.',
manualTruncated: 'Показаны первые 500 файлов — в папке есть ещё.',
manualShow: 'Шоу',
manualDetected: 'Определено по имени релиза — проверьте и поправьте, если не то.',
manualPickShow: 'Выберите шоу',
manualImport: 'Забрать в шоу',
manualImported: 'Импортировано файлов: {{count}}',
uploadToShow: 'Загрузить в шоу',
toShowTitle: 'Загрузить и добавить в шоу',
toShowPick: 'Выбрать файлы',
toShowEmpty: 'Файлы не выбраны',
toShowPicked: 'Выбрано файлов: {{count}}',
autoDetectHint:
'Каждый файл привяжется к шоу, чьё оригинальное (или отображаемое) название есть в имени релиза, напр. «The.Simpsons.S33E01…» → The Simpsons.',
toShowLibrary: 'В библиотеку',
toShowSeason: 'Сезон (вручную)',
toShowAuto: 'авто',
toShowRegex: 'Regex серии',
toShowRegexInvalid: 'некорректный regex',
toShowHint: String.raw`Сезон и regex — необязательны: обычно номера распознаются сами (см. ниже). Regex: 1 группа = серия, 2 группы = сезон и серия. Пример: ^(\d+) для «01. Название.mkv».`,
toShowPreview: 'Что распознаем',
toShowMatched: 'шоу распознано у {{matched}} из {{total}}',
applyToAll: 'Задать всем…',
toShowUnknown: '—',
toShowConfirm: 'Загрузить и добавить',
uploadedCount: 'Загружено файлов: {{count}}',
uploadingCount: 'Загрузка {{done}}/{{total}}',
cancelAll: 'Отменить все загрузки',
skippedDuplicates: 'Пропущено дубликатов: {{count}}',
queueDrained: 'Обработка завершена — очередь пуста',
filterActive: 'Активные',
filterAll: 'Все',
name: 'Файл',
status: 'Статус',
duration: 'Длительность',
resolution: 'Разрешение',
processingTime: 'Время обработки',
empty: 'Пока нет загруженных файлов',
statuses: {
Pending: 'В очереди',
Processing: 'Обработка',
Ready: 'Готов',
Failed: 'Ошибка',
},
stats: {
queued: 'Сейчас в очереди',
queuedShort: 'В очереди',
processing: 'Сейчас в обработке',
processingShort: 'В обработке',
average: 'Среднее время обработки (по недавним)',
averageShort: 'Ср. время',
eta: 'Примерное время до конца очереди: оставшаяся работа по среднему, делённая на число параллельных транскодов',
etaShort: 'Осталось',
etaSoon: 'меньше минуты',
hoursShort: 'ч',
minutesShort: 'мин',
},
},
gallery: {
title: 'Галерея',
upload: 'Загрузить',
empty: 'В этой категории пока нет изображений',
pickHint: 'Выберите изображение или загрузите новое',
browseHint: 'Все изображения приложения по категориям',
sort: {
newest: 'Сначала новые',
oldest: 'Сначала старые',
nameAsc: 'Имя: А–Я',
nameDesc: 'Имя: Я–А',
},
categories: {
Library: 'Библиотека',
ShowPoster: 'Постеры шоу',
EpisodeStill: 'Кадры серий',
BumperBackground: 'Фоны заставок',
},
},
shows: {
title: 'Шоу',
createTitle: 'Создать шоу',
name: 'Название',
originalName: 'Оригинальное название (eng)',
kind: 'Тип',
kinds: { Series: 'Сериал', Single: 'Полнометражка', Interstitial: 'Ролик' },
pickerPlaceholder: 'Выберите шоу',
pickerSearch: 'Поиск по названию…',
pickerEmpty: 'Ничего не найдено',
pickerCreate: 'Создать сериал «{{name}}»',
pickerCreateSingleHint: 'Создать как полнометражку',
pickerCreated: 'Шоу создано',
audience: 'Рейтинг',
audienceUnset: 'Не проставлен',
audiences: {
G: 'G — без ограничений',
PG: 'PG — с родителями',
'PG-13': 'PG-13 — с 13 лет',
R: 'R — с 17 со взрослым',
'NC-17': 'NC-17 — только взрослым',
},
seasons: 'Сезоны',
loadedSeasons: 'Загружены сезоны',
genre: 'Жанр',
allGenres: 'Все жанры',
allKinds: 'Все типы',
deleteTitle: 'Удалить шоу?',
deleteHint: '«{{name}}» будет удалено из библиотеки. Отменить это нельзя.',
deleteWithMedia: 'Также удалить медиа: файлов {{count}}',
deleteWithMediaHint:
'Иначе файлы останутся в списке медиа ничьими и продолжат занимать место.',
deleteWithSchedule: 'Также вырезать из будущего эфира',
deleteWithScheduleHint:
'Без этого удаление отклонят, пока шоу стоит в ленте. На месте вырезанного зритель увидит фон, пока хвост не пересоберут.',
otherGenres: 'Остальные жанры',
bulk: {
selected: 'Отмечено: {{count}}',
selectPage: 'Отметить все на странице',
noGenre: 'Жанр не менять',
keepAudience: 'Рейтинг не менять',
apply: 'Применить',
tagged: 'Обновлено шоу: {{count}}',
clear: 'Снять выбор',
enrich: 'Метаданные из {{provider}}',
enrichHint:
'Найти по названию и применить — только тем, у кого источник ещё не привязан. Неоднозначные совпадения пропускаются.',
enriching: 'Ищем…',
enriched: 'Метаданные применены: {{count}}',
moreSkipped: 'Ещё пропущено: {{count}}',
toCollection: 'В коллекцию…',
toCollectionHint:
'Отмеченные шоу уйдут во франшизу. Порядок частей — по году выпуска; поправить его можно перетаскиванием в карточке коллекции.',
newCollection: 'Новая коллекция',
addToCollection: 'Добавить ({{count}})',
addedToCollection: '«{{name}}» пополнена: {{count}}',
},
genresEmpty: 'Жанры не проставлены',
genresEdit: 'Жанры',
genresHint:
'Отметьте жанры шоу. Основной показывается в списке; в отборе контента участвуют все.',
genrePrimary: 'основной',
inGroups: 'В группах',
inChannels: 'В эфире каналов',
inCollections: 'Входит в коллекции',
episodes: 'Серии',
episode: 'Серия',
noEpisodes: 'Серий пока нет',
addMedia: 'Добавить медиа',
addMediaHint: 'Готовые файлы, ещё не добавленные в это шоу. Выбранные добавятся сериями.',
filterAssets: 'Фильтр по имени, напр. Family.Guy.S16',
selectAll: 'Выбрать все',
deselectAll: 'Снять все',
addSelected: 'Добавить выбранные',
noMatches: 'Нет подходящих файлов',
addedCount: 'Добавлено серий: {{count}}',
candidatesTruncated:
'Показаны не все файлы (слишком много готовых ассетов) — уточните фильтр, чтобы увидеть остальные.',
},
channels: {
number: 'Номер',
numberPlaceholder: 'не задан',
utcOffset: 'Часовой пояс, ч',
utcOffsetHint:
'Целыми часами: 3 — Москва, 0 — UTC, −5 — Нью-Йорк. В этом времени задаётся вся сетка и показывается расписание.',
dayStart: 'Начало вещательных суток',
dayStartHint: 'Ночной блок до этого времени относится к предыдущему дню.',
disabled: 'выключен',
layers: 'Слои',
layersHint: 'Побеждает слой с большим приоритетом. Фоновый лежит под всеми и закрывает дыры.',
background: 'фон',
newLayerName: 'Новый слой',
addSlotHere: 'Добавить слот',
newSlot: 'Новый слот',
/** Автосборка сетки по профилю: предпросмотр плана и его создание. */
transfer: {
action: 'Импорт / экспорт',
title: 'Обмен конфигурацией сетки',
hint: 'Файл ссылается на группы и стыки по именам, поэтому переносится между каналами и установками. Запрос к ИИ никуда не отправляется — вы копируете его в свою модель и приносите ответ назад вкладкой импорта.',
tabs: { export: 'Экспорт', import: 'Импорт', ai: 'Запрос к ИИ' },
exportHint:
'Выгружает слои, слоты, правила и ссылки на группы и стыки. Годится и как бэкап перед экспериментом, и как образец для модели.',
exportAction: 'Скачать файл',
exported: 'Файл выгружен',
importHint:
'Загрузите файл или вставьте JSON — например ответ модели. Недостающие группы и коллекции файл может принести с собой; шоу не создаются никогда — их приносит загрузка медиа. Незнакомые ссылки не валят импорт: такие слоты пропускаются, и вы увидите их списком.',
pickFile: 'Выбрать файл',
replace: 'Снести существующие слоты',
pastePlaceholder: 'Вставьте JSON конфигурации сетки',
importAction: 'Загрузить',
imported: 'Создано слотов: {{slots}}, пропущено: {{skipped}}',
importedLibrary: 'Заведено групп: {{groups}}, коллекций: {{collections}}',
badJson: 'Это не похоже на JSON — проверьте, что скопирован весь ответ целиком.',
warnings: 'Что не удалось разобрать',
aiHint:
'Соберём запрос: параметры канала, ваши группы и библиотека, ваши референсы и пожелания, плюс схема ответа. Модель может собрать нужные ей группы сама — они заведутся при импорте.',
references: 'Референс-каналы',
referencesPlaceholder: '2×2, Paramount Comedy',
referencesHint:
'Через запятую. На их ритм модель будет равняться — длина блоков, прайм, ночь.',
notes: 'Пожелания',
notesPlaceholder: 'Например: утром детское, вечером кино, ночью — взрослый блок',
buildPrompt: 'Собрать запрос',
copyPrompt: 'Скопировать',
copied: 'Запрос скопирован',
aiNext: 'Отправьте это в свою модель, а полученный JSON вставьте во вкладке «Импорт».',
},
generate: {
action: 'Собрать сетку',
title: 'Автосборка сетки',
hint: 'Профиль задаёт ритм суток: длину блоков, что стоит в прайме и чем закрыта ночь. Группы под каждую полосу подбираются из существующих — по рейтингу, типу контента и запасу серий.',
modes: {
FillGaps: 'Заполнить дыры',
Rebuild: 'Собрать неделю с нуля',
},
modeHints: {
FillGaps: 'Существующие слоты не трогаются — застраивается только незакрытое время.',
Rebuild: 'Все слоты шаблона сносятся; выходные строятся отдельным слоем поверх будних.',
},
summary: 'Будет создано слотов: {{slots}} — это {{percent}}% свободного времени недели.',
willRemove: 'Будет снесено слотов: {{count}}.',
fallback: 'Аварийной группой станет «{{name}}» — ею закрываются паузы между слотами.',
nothing: 'Создавать нечего: свободного времени в сетке нет.',
create: 'Собрать',
done: 'Создано слотов: {{created}}, снесено: {{removed}}.',
},
editSlot: 'Слот',
slotTitle: 'Название блока',
slotStart: 'Начало',
slotDuration: 'Бюджет, мин',
weekday: 'День недели',
everyDay: 'каждый день',
daypart: 'Дейпарт',
slotKind: 'Тип слота',
group: 'Группа',
pickGroup: 'Выберите группу',
strategy: 'Стратегия',
cooldownDays: 'Остывание, дней',
cooldownHint: 'Не брать то, что уже выходило за этот срок.',
blockMode: 'Блок',
blockValue: 'Значение',
overflow: 'Если не помещается',
overflowHint: 'Как поступить с элементом, который не влезает в бюджет слота.',
repeatDaysAgo: 'Суток назад',
repeatTime: 'Время',
repeatDuration: 'Длительность, мин',
anchor: 'Якорь',
anchorHint:
'Якорь держит старт жёстко: через него не переносят то, что не влезает. Округление мягкое — сдвигает старт до круглого времени, если это укладывается в допуск.',
maxDrift: 'Допуск, мин',
snap: 'Округление',
snapOff: 'нет',
resizeSlot: 'Потянуть за край — длительность',
copyDay: 'Копировать день',
copyDayFrom: 'Копировать {{day}} в:',
copy: 'Копировать',
layerVisible: 'Показывать слой',
layerName: 'Название слоя',
layerApplicability: 'Когда действует',
applicabilityHint:
'Разделы объединяются по ИЛИ: слой действует, если дата подходит хотя бы под одно условие. Ничего не заполнено — действует всегда.',
applicabilityWeekdays: 'Дни недели',
applicabilityDateRanges: 'Диапазоны дат',
applicabilityAnnual: 'Ежегодно (месяц / день)',
applicabilityDates: 'Конкретные даты',
applicabilityNone: 'не задано',
showForDate: 'Сетка на дату',
allDates: 'Все слои',
tabs: {
grid: 'Сетка',
rules: 'Правила',
junctions: 'Стыки',
viewer: 'Зритель',
settings: 'Настройки',
air: 'Эфир',
},
noTemplate: 'Сетка канала не загрузилась',
createTemplate: 'Создать сетку',
createTemplateHint: 'Появится пустая сетка с фоновым слоем — дальше добавляйте слои и слоты.',
rules: 'Правила отбора',
rulesHint:
'Жёсткие фильтры: отсекают неподходящее до жребия. Как и правка сетки, эфир не двигают — нужно применить.',
audienceWindows: 'Детское время',
noAudienceWindows: 'Окон нет — возраст ничем не ограничен.',
audienceWindowsHint:
'В окне в эфир идёт контент не строже выбранной категории. Окно может переходить через полночь. Контент без категории не отсекается.',
from: 'С',
to: 'До',
maxAudience: 'Не строже',
repeatLimit: 'Потолок повторов',
repeatWindowDays: 'Окно, суток',
repeatMax: 'Не чаще, раз',
repeatLimitHint:
'Считается по уже записанной ленте. Если потолка достигли все кандидаты, слот всё равно заполняется: пустой эфир хуже раннего повтора.',
preview: 'Предпросмотр',
previewHide: 'Свернуть предпросмотр',
previewHint: 'Прогон по текущим правилам: ничего не пишется, курсоры слотов не двигаются.',
previewDays_one: '{{count}} сутки',
previewDays_few: '{{count}} суток',
previewDays_many: '{{count}} суток',
previewTabs: { programme: 'Программа', tape: 'Лента', problems: 'Проблемы' },
noProblems: 'Проблем нет',
andMore: 'и ещё {{count}}',
heatmap: 'Повторы: шоу × сутки',
heatmapTotal: 'всего',
issues: 'Проверки: ошибок {{errors}}, предупреждений {{warnings}}',
goToSlot: 'к слоту',
issueKinds: {
GroupEmpty: 'Пустая группа',
GroupTooSmall: 'Мало контента',
GridGap: 'Дыра в сетке',
SlotOverlap: 'Слоты пересекаются',
CooldownUnreachable: 'Недостижимое остывание',
AudienceConflict: 'Возрастной конфликт',
GroupMissing: 'Группа не выбрана',
},
viewer: 'Как выглядит у зрителя',
viewerHint:
'Оверлеи рисуются поверх картинки на клиенте — видео не перекодируется. Всё по умолчанию выключено.',
logo: 'Логотип',
noLogo: 'нет',
pickLogo: 'Выбрать логотип',
logoCorner: 'Угол',
logoOpacity: 'Прозрачность',
corners: {
TopLeft: 'Слева вверху',
TopRight: 'Справа вверху',
BottomLeft: 'Слева внизу',
BottomRight: 'Справа внизу',
},
showClock: 'Показывать часы',
analogFilter: 'Аналоговый фильтр',
analogFilterHint: 'Сила 0..1. Ноль — выключен; переборщить очень легко.',
whyHere: 'Почему это здесь',
priority: 'приоритет',
traceLayer: 'Слой',
traceSlot: 'Слот',
traceGroup: 'Группа',
traceCollection: 'Коллекция',
traceStrategy: 'Стратегия',
traceJunction: 'Врезки',
traceDrift: 'дрейф {{minutes}} мин',
traceSnapped: 'старт округлён',
traceCooldown: 'остывание {{days}} дн.',
traceCandidates: 'кандидатов после остывания: {{count}}',
diffSummary: 'Затронет {{total}} записей, изменятся {{changed}}',
diffSoon: 'В ближайшие сутки изменится записей: {{count}}',
diffNoChanges: 'Эфир не изменится',
copyTemplate: 'Скопировать сетку с канала',
pickSourceChannel: 'Выберите канал-источник',
copyHere: 'Скопировать сюда',
copyTemplateHint:
'Слои, слоты, стыки и правила выбранного канала заменят сетку этого канала. Группы общие и не копируются.',
copyTemplateConfirm:
'Текущая сетка этого канала будет заменена сеткой выбранного канала. Продолжить?',
templateCopied: 'Скопировано: слоёв {{layers}}, слотов {{slots}}',
copyDroppedBumpers: 'Врезок без блока заставки: {{count}} — донастройте руками',
// Не «Сбросить»: так уже называется откат правок к эфирному состоянию (ключ restore).
reset: 'Очистить сетку',
resetConfirm:
'Сетка канала будет снята полностью — все слои и слоты, — а будущий эфир, собранный по ней, удалён. Прошлое и идущая сейчас запись останутся. Продолжить?',
resetDone: 'Сброшено: слоёв {{layers}}, слотов {{slots}}, записей эфира {{entries}}',
resetHint:
'Пока канал включён, лента отстроится заново по пустой сетке — аварийной группой. Чтобы эфир остался пустым, снимите её в правилах или выключите канал.',
fallbackGroup: 'Аварийная группа',
fallbackGroupNone: 'Нет — только филлер канала',
fallbackGroupHint:
'Чем закрывается пауза, если пуст и фоновый слой. Её же играет пустая сетка — весь эфир, пока слотов нет. Проставляется автосборкой сетки, если своя не выбрана.',
postChecks: 'Пост-проверки',
breakLimit: 'Потолок врезок в час, мин',
genreShare: 'Потолок доли жанра за сутки, %',
fallbackShare: 'Потолок доли фона, %',
postChecksHint:
'Пост-проверки считаются по готовой ленте и только предупреждают — ничего не переигрывается.',
previewKinds: {
Program: 'Программа',
Fallback: 'Фон',
SignOff: 'Конец вещания',
Ad: 'Реклама',
Promo: 'Анонс',
Bumper: 'Заставка',
},
previewLoad: 'Врезки по часам, пик — {{peak}} мин',
junctions: 'Стыки',
junctionsHint:
'Что играет между программами: реклама, анонсы, заставки. Слот может взять свой стык, иначе берётся стык по умолчанию.',
defaultJunction: 'Стык по умолчанию',
noJunction: 'без стыка',
junctionBetween: 'Стык внутри слота',
junctionAfter: 'Стык после слота',
junctionDefault: 'по умолчанию',
junctionsUsed: 'Используются в этом канале',
junctionsNoneUsed: 'Канал не ссылается ни на один стык — между программами ничего не играет.',
openJunctionEditor: 'Редактор стыков',
pendingChanges: 'Правила изменены — эфир идёт по старым.',
restore: 'Сбросить изменения',
restoreConfirm:
'Вернуть сетку к тому состоянию, по которому идёт эфир? Всё, что изменено после последнего применения, будет потеряно.',
restored: 'Сетка возвращена к эфирной: слотов — {{count}}.',
restoreDropped:
'Ссылок не восстановлено: {{count}} — группа или стык были удалены после применения.',
apply: 'Применить',
applied: 'Эфир пересобран, записей: {{count}}',
applyReport: 'Отчёт о применении',
applyReportWarnings:
'Планировщик выполнил не все правила — предупреждений: {{count}}. Эфир собран, но эти места стоит посмотреть.',
weekdays: {
0: 'Вс',
1: 'Пн',
2: 'Вт',
3: 'Ср',
4: 'Чт',
5: 'Пт',
6: 'Сб',
},
dayparts: { Morning: 'Утро', Day: 'День', Prime: 'Прайм', Night: 'Ночь' },
slotKinds: { Content: 'Контент', Repeat: 'Повтор', SignOff: 'Конец вещания' },
blockModes: { Count: 'Единиц', Duration: 'Минут', FillSlot: 'Весь слот' },
overflows: {
ContinueNext: 'Доиграть в следующий раз',
ExtendSlot: 'Растянуть слот',
SkipIfNotFits: 'Не начинать',
},
strategies: {
Sequential: 'По порядку',
RandomWithCooldown: 'Случайно с остыванием',
Fixed: 'Фиксированный элемент',
},
warnings: {
SlotEmpty: 'Слот не дал контента',
DriftExceeded: 'Старт ушёл за допуск',
CooldownExhausted: 'Остывание отсекло всех',
RepeatSourceEmpty: 'Нечего повторять',
FallbackEmpty: 'Нечем закрыть паузы',
CandidatesFiltered: 'Возрастной потолок отсёк всех',
BreakLimitExceeded: 'Врезок в часе больше потолка',
GenreShareExceeded: 'Доля жанра выше нормы',
FallbackShareExceeded: 'Аварийного запаса в эфире больше нормы',
},
title: 'Каналы',
createTitle: 'Создать канал',
name: 'Название',
slug: 'Slug',
state: 'Состояние',
enabled: 'В эфире',
enabledLabel: 'Канал в эфире',
settings: 'Настройки',
filler: 'Заглушка',
noFiller: 'Без заглушки',
noSchedule: 'Расписание ещё не построено',
airToday: 'Сегодня',
airCount: 'Записей за сутки: {{count}}',
airHidePast: 'Скрывать прошедшее',
airHideBreaks: 'Скрывать врезки',
},
junctions: {
title: 'Стыки',
hint: 'Что играет между программами: реклама, анонсы, заставки. Стык общий для всех каналов — канал только выбирает, какой поставить в слот.',
newName: 'Новый стык',
empty0: 'Стыков пока нет.',
empty: 'пусто',
from: 'конец',
to: 'начало',
fork: 'развилка',
addElement: '+ врезка',
maxTotal: 'Потолок, с',
usedInChannels: 'в каналах: {{count}}',
cannotDeleteUsed: 'Стык используется каналами — сначала снимите ссылки.',
splitOut: 'Вынести из развилки',
mergeIntoFork: 'Объединить с предыдущей в развилку',
element: 'Врезка',
kind: 'Тип',
kinds: {
Ad: 'Реклама',
Promo: 'Анонс',
Bumper: 'Заставка',
Filler: 'Заполнитель',
},
group: 'Группа',
pickGroup: 'выберите группу',
bumperTemplate: 'Блок заставки',
pickBumper: 'выберите блок',
bumperVariant: 'Подблок',
bumperVariantAuto: 'по триггеру и весам',
bumperVariantHint:
'Оставьте «по триггеру и весам», чтобы на смене шоу и между сериями играли разные тексты.',
amountMode: 'Чем меряется',
amountModes: { Count: 'Единиц', Duration: 'Минут' },
count: 'Сколько единиц',
minutes: 'Сколько минут',
minutesShort: ' мин',
amountHint:
'В смешанной группе (ролики и готовые блоки) считайте минутами: одна «единица» там — то ли ролик, то ли блок.',
required: 'Обязательная',
requiredHint:
'Обязательность решает, кого выбросить при нехватке времени. На порядок показа она не влияет — он задаётся местом в цепочке.',
choiceWeight: 'Вес в развилке',
choiceWeightHint: 'Чем больше — тем чаще играет именно эта врезка (0 — не выбирается).',
conditions: 'Условия показа',
onlyOnChange: 'Только при смене шоу',
chance: 'Вероятность, %',
chanceHint:
'Интервал считается по этой врезке отдельно. У развилки жребий один на всех: бросает выбранный вариант, и его вероятность — вероятность всей развилки. Жребий берётся из seed генерации, поэтому пересборка не тасует врезки.',
minInterval: 'Не чаще, чем раз в, мин',
dayparts: 'Дейпарты',
daypartsHint: 'Ничего не выбрано — врезка идёт в любых дейпартах.',
timeWindow: 'Окно суток канала',
clearWindow: 'Сбросить',
timeWindowHint: 'Пусто — в любое время. Окно может переходить через полночь.',
nearHour: 'Рядом с круглым часом',
nearHourUnit: 'мин до и после :00',
nearHourHint:
'0 — без привязки. Сигнал точного времени и джингл ставятся так: врезка играет только в окрестности :00, а не когда-нибудь в этом часе.',
badgeNearHour: '±{{minutes}} мин от часа',
badgeOnChange: 'на смене',
badgeInterval: 'раз в {{minutes}} мин',
},
bumpers: {
title: 'Заставки',
hint: 'Блоки заставок общие для всех каналов: оформление и звук — у блока, текст — у подблоков. В эфир заставка попадает врезкой стыка.',
empty: 'Блоков заставок пока нет.',
newName: 'Название блока',
createTitle: 'Создать блок заставки',
newNamePlaceholder: 'Название блока',
newVariantName: 'Новый текст',
sampleChannel: 'Смотреть глазами канала',
sampleChannelNone: 'без канала',
sampleChannelHint: 'Блок общий, но образцы подстановки берутся у выбранного канала.',
name: 'Название',
font: 'Шрифт',
fontSans: 'Гротеск',
fontSerif: 'Антиква',
colorBg: 'Фон (цвет 1)',
colorBg2: 'Фон (цвет 2)',
colorAccent: 'Акцент',
colorText: 'Текст',
saveStyle: 'Сохранить оформление',
seconds: 'с',
defaultDuration: '≈8 с (джингл)',
variantsCount: 'текстов: {{count}}',
usedInJunctions: 'во врезках: {{count}}',
cannotDeleteUsed: 'Блок используется во врезках стыков — сначала снимите ссылки.',
audio: 'Звук',
audioHint: 'Длина звука задаёт длительность заставки; без него — синтезированный джингл.',
background: 'Фон',
backgroundFieldHint: 'Картинка фона блока; иначе — градиент палитры или постер шоу.',
backgroundPick: 'Выбрать из галереи',
backgrounds: {
Template: 'Фон блока',
NextPoster: 'Постер следующего шоу',
NowPoster: 'Постер предыдущего шоу',
Clip: 'Готовый ролик',
},
backgroundClip: 'Ролик-подложка',
pickClip: 'Выберите ролик',
backgroundClipHint:
'Текст пишется поверх видео. Длину заставки задаёт сам ролик; звук блока подрезается под неё, а без звука играет оригинальная дорожка ролика.',
fileLoaded: 'загружено',
fileDefault: 'по умолчанию',
upload: 'Загрузить',
reset: 'Сбросить',
variants: 'Подблоки (тексты)',
variantsHint:
'Разный текст на одной музыке и оформлении. Правило показа и фон — у каждого подблока свои.',
addVariant: 'Добавить текст',
variantName: 'Название',
trigger: 'Показывать',
triggers: {
OnShowChange: 'При смене шоу',
BetweenEpisodes: 'Между сериями',
Both: 'Оба',
},
weight: 'Вес',
lineStyles: { Label: 'Подпись', Title: 'Название', Caption: 'Мелкая' },
lineColors: { Accent: 'Акцент', Text: 'Текст' },
addLine: 'Строка',
presets: 'Пресеты',
preset_nowNext: 'Сейчас / Далее',
preset_nextAt: 'Далее в …',
preset_channel: 'Логотип канала',
presetNow: 'СЕЙЧАС',
presetNext: 'ДАЛЕЕ',
presetNextAt: 'ДАЛЕЕ В',
framePreview: 'Кадр',
framePreviewHint: 'Подстановка образцами: так строка будет выглядеть в эфире.',
unknownPlaceholder: 'Неизвестный плейсхолдер: {{tokens}}',
volatileHint: 'каждый показ уникален — кэш рендера не работает',
render: 'Отрендерить примеры',
rendering: 'Рендерим…',
renderHint: 'Настоящий ffmpeg-рендер всех подблоков со звуком — несколько секунд.',
tokens: {
channel: 'Название канала',
'channel.number': 'Номер канала',
'now.title': 'Текущее шоу',
'next.title': 'Следующее шоу',
'now.episode': 'Серия текущего',
'next.episode': 'Серия следующего',
'next.year': 'Год следующего',
'next.genre': 'Жанр следующего',
'next.time': 'Время старта следующего',
'tonight.title': 'Вечерняя программа',
'tonight.time': 'Время вечерней программы',
'tomorrow.title': 'Программа завтра вечером',
'tomorrow.time': 'Время программы завтра',
time: 'Время показа заставки',
date: 'Дата',
weekday: 'День недели',
slot: 'Название слота',
},
},
storage: {
title: 'Хранилище',
volume: 'Том хранилища',
volumeUnknown: 'Файловая система не отдала размер тома — показано только занятое хранилищем.',
totalVolume: 'Всего на томе',
ours: 'Занято TeleWave',
foreign: 'Прочее на томе',
foreignHint: 'база, система, чужие файлы',
free: 'Свободно',
lowSpace:
'Свободного места меньше порога ({{threshold}}) — загрузка новых файлов отклоняется.',
recount: 'Пересчитать',
computedAt: 'посчитано в {{time}}',
breakdown: 'Из чего складывается',
filesTotal: 'файлов: {{count}}',
files: 'файлов: {{count}}',
empty: 'Хранилище пустое.',
areas: {
Assets: 'Сегменты программ',
BumperAssets: 'Сегменты заставок',
Originals: 'Исходники',
Inbox: 'Inbox',
ManualInbox: 'Ручной inbox',
Uploads: 'Незавершённые загрузки',
BumperSources: 'Звук и фоны заставок',
Images: 'Изображения',
Other: 'Прочее под корнем',
},
areaHints: {
Assets: 'HLS-нарезка библиотеки — обычно почти весь объём',
BumperAssets: 'отрендеренные заставки; чистятся вместе с расписанием',
Originals: 'остаются, только если включён Storage:KeepOriginals',
Inbox: 'то, что ещё не разобрал сканер',
ManualInbox: 'то, что забирается в шоу вручную',
Uploads: 'перевалочный каталог; залежавшееся — след оборванной заливки',
BumperSources: 'сырые файлы блоков, не нарезка',
Images: 'постеры, кадры, логотипы',
Other: 'файлы мимо известных каталогов — обычно ноль',
},
},
maintenance: {
title: 'Обслуживание',
warning: 'Операции необратимы — удаляют данные и файлы навсегда.',
clearMedia: 'Очистить все медиа',
clearMediaHint: 'Удалит все загруженные файлы и их нарезку, а также расписание каналов.',
confirmClearMedia: 'Удалить ВСЕ медиа-файлы безвозвратно?',
clearShowMedia: 'Удалить медиа шоу',
clearShowMediaHint: 'Удалит файлы всех серий выбранного шоу и очистит его серии.',
confirmClearShowMedia: 'Удалить все медиа выбранного шоу безвозвратно?',
pickShow: 'Выберите шоу',
deleteShows: 'Удалить все шоу',
deleteShowsHint: 'Удалит все шоу и их серии. Медиа-файлы останутся в библиотеке.',
confirmDeleteShows: 'Удалить ВСЕ шоу безвозвратно?',
doneCount: 'Удалено: {{count}}',
},
settings: {
title: 'Настройки',
registration: 'Регистрация',
registrationHint:
'Когда выключено — новые пользователи не могут регистрироваться сами, учётки заводит только администратор.',
registrationLabel: 'Разрешить регистрацию на сайте',
preferredAudio: 'Предпочитаемые озвучки',
channelNumbers: 'Переключение каналов по номерам',
channelNumbersHint:
'Зритель переключает каналы стрелками, как на телевизоре. Сетка каналов остаётся всегда.',
preferredAudioHint:
'Коды языков через запятую в порядке приоритета (напр. «rus, eng»). Если в файле есть дорожка с таким языком — при обработке выбирается она (по порядку); иначе — выбор ffmpeg по умолчанию. Применяется к новым обработкам.',
},
metadata: {
title: 'Метаданные',
pickPoster: 'Из галереи',
name: 'Название',
originalName: 'Оригинальное название (eng)',
originalNamePlaceholder: 'Например: Family Guy',
originalNameHint: 'По нему ищутся метаданные; на экранах показывается обычное название.',
sourceLabel: 'Источник',
searchBtn: 'Искать',
nothingFound: 'Ничего не найдено',
apply: 'Применить',
applied: 'Метаданные применены',
overview: 'Описание',
year: 'Год',
clear: 'Очистить',
noPoster: 'Нет постера',
refreshEpisodes: 'Обновить серии',
refreshing: 'Обновляем…',
refreshedCount: 'Обновлено серий: {{count}}',
findMissing: 'Найти отсутствующие серии',
missingTitle: 'Отсутствующие серии',
missingNoSeasons: 'В шоу нет загруженных серий с распознанными номерами.',
seasonN: 'Сезон {{n}}',
loadedOf: 'загружено {{loaded}} из {{total}}',
missingUnknown: 'Источник не отдал число серий этого сезона.',
missingNone: 'Все серии на месте.',
missingList: 'Не хватает',
findMissingSeasons: 'Найти отсутствующие сезоны',
missingSeasonsTitle: 'Отсутствующие сезоны',
missingSeasonsUnknownTotal: 'Источник не отдал список сезонов этого сериала.',
missingSeasonsNone: 'Все сезоны на месте.',
missingSeasonsList: 'Не загружено ни одной серии из сезонов',
missingSeasonsUnknown: 'Источник не знает загруженные сезоны: {{list}}.',
},
},
}