export type EditorLocale = 'ru' | 'en';

export const EDITOR_LOCALE_STORAGE_KEY = 'dnd_editor_locale';

function primaryLanguageTag(lang: string): string {
  const trimmed = lang.trim().toLowerCase();
  if (!trimmed) return '';
  const sep = trimmed.search(/[-_]/);
  return sep === -1 ? trimmed : trimmed.slice(0, sep);
}

/**
 * Выбор `ru` / `en` по языку ОС/браузера, если пользователь ещё не сохранил язык в `localStorage`.
 * В Electron совпадает с локалью системы (Chromium подставляет `navigator.languages`).
 */
export function inferEditorLocaleFromSystem(languages?: readonly string[]): EditorLocale {
  let list: string[];
  if (languages !== undefined) {
    list = [...languages];
  } else if (typeof navigator !== 'undefined') {
    list = [...navigator.languages];
    if (navigator.language) {
      list.push(navigator.language);
    }
    list = list.filter((x) => x.trim() !== '');
  } else {
    list = [];
  }
  for (const lang of list) {
    const tag = primaryLanguageTag(lang);
    if (tag === 'en') return 'en';
    if (tag === 'ru') return 'ru';
  }
  return 'ru';
}

export function normalizeEditorLocale(raw: string | null | undefined): EditorLocale {
  if (raw == null) {
    return inferEditorLocaleFromSystem();
  }
  const trimmed = raw.trim();
  if (trimmed === '') {
    return inferEditorLocaleFromSystem();
  }
  const s = trimmed.toLowerCase();
  if (s === 'en') return 'en';
  if (s === 'ru') return 'ru';
  return inferEditorLocaleFromSystem();
}

/** Flat message table; `{name}` placeholders supported in `translate`. */
export const EDITOR_MESSAGES: Record<EditorLocale, Record<string, string>> = {
  ru: {
    'common.close': 'Закрыть',
    'common.cancel': 'Отмена',
    'common.save': 'Сохранить',
    'common.saving': 'Сохранение…',
    'common.edit': 'Редактировать',
    'common.understood': 'Понятно',
    'common.message': 'Сообщение',
    'common.error': 'Ошибка',
    'common.delete': 'Удалить',
    'common.closeMenu': 'Закрыть меню',

    'app.brandTitle': 'НРИ Плеер',

    'notice.campaignAudioEmpty': 'Аудио не добавлено. Проверьте формат файла.',

    'license.checkingTitle': 'Проверка лицензии…',
    'license.checkingWait': 'Подождите.',
    'license.requiredTitle': 'Требуется лицензия',
    'license.requiredHint':
      'Укажите ключ в меню «Настройки» → «Указать ключ». До активации доступно только меню «Настройки».',
    'license.tokenTitle': 'Указать ключ',
    'license.tokenKey': 'КЛЮЧ',
    'license.tokenPlaceholder': 'Продуктовый ключ TTRPG-... или DND-...',
    'license.tokenSaving': 'Сохранение…',
    'license.eulaTitle': 'Лицензионное соглашение',
    'license.eulaReject': 'Не принимаю',
    'license.eulaAccept': 'Принимаю условия',
    'license.eulaNoteEn':
      'The binding legal text below is in Russian. If you need an English summary, contact support.',
    'license.aboutTitle': 'О лицензии',
    'license.aboutDevSkip': 'Режим разработки: проверка лицензии отключена (DND_SKIP_LICENSE).',
    'license.aboutStatus': 'СТАТУС',
    'license.aboutProduct': 'ПРОДУКТ',
    'license.aboutLicenseId': 'ID ЛИЦЕНЗИИ',
    'license.aboutExpiry': 'ОКОНЧАНИЕ',
    'license.aboutDevice': 'УСТРОЙСТВО',
    'license.aboutNoData': 'Нет данных лицензии.',
    'license.reason.ok': 'Активна',
    'license.reason.none': 'Ключ не указан',
    'license.reason.expired': 'Срок действия истёк',
    'license.reason.bad_signature': 'Недействительная подпись',
    'license.reason.bad_payload': 'Неверный формат токена',
    'license.reason.malformed': 'Повреждённый токен',
    'license.reason.not_yet_valid': 'Ещё не действует',
    'license.reason.wrong_device': 'Другой привязанный компьютер',
    'license.reason.revoked_remote': 'Отозвана на сервере',

    'presentation.overlay': 'Презентация запущена',
    'presentation.title': 'Презентация запущена',
    'presentation.body':
      'Редактор заблокирован. Закройте окна «Презентация» и «Панель управления», чтобы продолжить.',

    'zip.progress': 'Прогресс операции',
    'zip.importTitle': 'Импорт проекта',
    'zip.exportTitle': 'Экспорт проекта',

    'top.settings': 'Настройки',
    'top.project': 'Проект',
    'top.file': 'Файл',
    'top.players': 'Игроки',
    'top.backToProjects': 'К списку проектов',
    'top.appVersion': 'Версия приложения',
    'top.run': 'Запустить',
    'top.runMenuAria': 'Дополнительные варианты запуска',
    'top.runWithPlayers': 'Запустить с игроками',
    'top.runWithPlayersTitle': 'Запуск с игроками',
    'top.runWithPlayersHint': 'Выберите игроков и/или команды. Выбор сохранится на время сессии.',
    'top.runWithPlayersTeams': 'Команды',
    'top.runWithPlayersPlayers': 'Игроки',
    'top.runWithPlayersEmpty': 'Нет игроков в библиотеке.',
    'top.runWithPlayersConfirm': 'Запустить',
    'top.launching': 'Запуск…',
    'top.afterLicense': 'Доступно после активации лицензии',
    'top.setStartScene': 'Назначьте начальную сцену на графе (ПКМ по узлу)',
    'top.runHelpAria': 'Как разблокировать кнопку «Запустить»',

    'menu.enterKey': 'Указать ключ',
    'menu.aboutLicense': 'О лицензии',
    'menu.checkUpdates': 'Проверить обновления',
    'menu.language': 'Язык',
    'menu.langRu': 'Русский',
    'menu.langEn': 'English',
    'menu.aboutProgram': 'О программе',
    'menu.instructions': 'Инструкция',

    'top.aboutApp': 'О приложении',

    'app.about.title': 'О программе',
    'app.about.tagline': 'Редактор кампаний и пульт мастера для настольных ролевых игр',
    'app.about.description':
      'TTRPG Player (НРИ Плеер) — настольное приложение для мастера: вы собираете кампанию как граф сцен с картами, музыкой и ветвлениями, а во время сессии управляете атмосферой с пульта. Игроки видят только презентацию на втором экране — без панелей редактора.',
    'app.about.versionLabel': 'ВЕРСИЯ',
    'app.about.developerLabel': 'РАЗРАБОТЧИК',
    'app.about.developer':
      'Независимая разработка. По вопросам лицензии, покупки и поддержки — электронная почта ниже.',
    'app.about.supportLabel': 'ПОДДЕРЖКА',
    'app.about.supportEmail': 'player.ttrpg@gmail.com',
    'app.about.websiteLabel': 'САЙТ И ОБНОВЛЕНИЯ',
    'app.about.websiteUrl': 'https://ttrpgplayer.ru/',

    'help.title': 'Инструкция',
    'help.navAria': 'Разделы инструкции',

    'help.section.overview.title': 'Обзор приложения',
    'help.section.overview.body':
      'При запуске вы попадаете в Редактор — здесь готовите кампанию: сцены, картинки, музыку и связи между эпизодами. Когда придёт время играть, нажмите «Запустить» — откроются «Презентация» для игроков и «Пульт управления» для вас.\n\nВ редакторе слева — список сцен, по центру — карта связей, справа — настройки игры и выбранной сцены.\n\nПока идёт показ, редактор временно нельзя менять — это нормально. Закройте презентацию и пульт, чтобы снова редактировать кампанию.\n\nИнтернет нужен только для активации лицензии и проверки обновлений. Все проекты и файлы хранятся на вашем компьютере.',

    'help.section.license.title': 'Лицензия и первый запуск',
    'help.section.license.body':
      'Перед работой с проектами один раз активируйте лицензию.\n\n1) Откройте «Настройки» → «Указать ключ».\n\n2) Если появится лицензионное соглашение — прочитайте и примите его.\n\n3) Вставьте ключ из письма (формат TTRPG-… или старый DND-…) и нажмите «Сохранить».\n\nДо активации доступны только настройки. После успешного сохранения ключа откроются проекты, сцены и запуск сессии.\n\nСтатус, срок действия и привязка к компьютеру — в «Настройки» → «О лицензии». Ключ привязан к этому ПК; на другом компьютере понадобится отдельная активация по условиям покупки.',

    'help.section.projects.title': 'Проекты',
    'help.section.projects.body':
      'Проект — вся кампания целиком: сцены, медиа и связи между ними.\n\nСоздать новую кампанию:\n\n1) На начальном экране введите название в поле слева.\n\n2) Нажмите «Создать проект».\n\nОткрыть существующую — кликните по названию в списке. Вернуться к списку: «Проект» → «Начальный экран» или клик по названию приложения в шапке.\n\nПеренести кампанию на другой компьютер:\n\n1) «Проект» → «Экспорт» — сохраните копию как .ttrpg.zip.\n\n2) На другом ПК — «Проект» → «Импорт» и выберите этот файл.\n\nПереименовать открытый проект: «Файл» → «Переименовать проект» (минимум 3 символа; в имени файла нельзя использовать <>:"/\\|?*).\n\nУдалить проект с диска:\n\n1) На карточке проекта нажмите «⋮».\n\n2) В меню выберите «Удалить».\n\n3) Подтвердите удаление в диалоге.\n\nПосле этого файл проекта и кэш будут стёрты без восстановления. Если кампания может понадобиться снова — сначала сделайте экспорт.',

    'help.section.scenes.title': 'Сцены',
    'help.section.scenes.body':
      'Сцена — отдельный эпизод: локация, кадр истории, диалог. У неё есть название, картинка или видео для игроков, описание для мастера и своя музыка.\n\nДобавить сцену:\n\n1) В левой колонке нажмите «+ Новая сцена».\n\n2) Задайте название и настройте свойства справа (см. «Свойства сцены»).\n\n«Поиск сцен…» помогает быстро найти нужную. Клик по карточке выделяет сцену — она же подсветится на карте связей.\n\nУдалить: правый клик по карточке в списке → «Удалить». Сцена исчезнет из списка и с карты вместе со связями.\n\nПеретащите сцену из списка на карту — так она появится как узел (подробнее в «Граф сцен»).',

    'help.section.graph.title': 'Граф сцен',
    'help.section.graph.body':
      'Карта в центре показывает, как эпизоды связаны. Каждый прямоугольник — место на схеме; одна сцена может встретиться несколько раз (например, игроки снова возвращаются в таверну).\n\nДобавить сцену на карту:\n\n1) Возьмите сцену в левом списке.\n\n2) Перетащите на свободное место на карте.\n\nСделать переход между сценами:\n\n1) Наведите на нижнюю точку первой карточки.\n\n2) Потяните линию к верхней точке второй и отпустите — появится стрелка.\n\nИз одной сцены может выходить несколько стрелок — так вы делаете ветвление сюжета. Вторую стрелку к той же паре сцен провести нельзя.\n\nУдалить стрелку: правый клик по линии → «Удалить». Обычный клик по линии ничего не делает.\n\nС чего начинается игра: правый клик по карточке → «Начальная сцена» (появится метка «НАЧАЛО»), затем «Запустить» в шапке. Можно начать с любой карточки основного сюжета: ПКМ → «Запустить с этой сцены» — откроются презентация и пульт с выбранного места (метку «НАЧАЛО» ставить не обязательно). Для карточек побочных линий этот пункт недоступен.\n\nУбрать карточку с карты, не удаляя сцену из списка: ПКМ → «Удалить».\n\nМасштаб — кнопки внизу или колёсико мыши. «Показать всё» вместит всю схему на экран.',

    'help.section.sideStorylines.title': 'Побочные сюжетные линии',
    'help.section.sideStorylines.body':
      'Побочная линия — отдельная ветка сюжета, не связанная с основным сюжетом. Она нужна для ответвлений, флешбэков, побочных квестов и сцен «вне основного пути».\n\nСоздать побочную линию в редакторе:\n\n1) Добавьте сцены на карту и соедините их стрелками в отдельной группе — она не должна касаться основного сюжета (фиолетовые связи) и других побочных линий.\n\n2) Правый клик по стартовой карточке побочной ветки → «Начальная сцена побочной линии». Появится синяя метка «ПОБОЧНАЯ».\n\n3) В свойствах сцены задайте «Название побочной линии» — оно будет видно на пульте.\n\nПункт «Начальная сцена побочной линии» скрыт, если карточка уже связана с основным сюжетом (где есть фиолетовое «НАЧАЛО») или с другой побочной линией (где есть синее «ПОБОЧНАЯ»).\n\nСвязи внутри побочной линии и выделение её карточек — синего цвета (#0078d4). Между основным и побочным сюжетом, а также между разными побочными линиями, стрелки провести нельзя.\n\nСнять метку: ПКМ → «Снять метку «Начальная сцена побочной линии»». Название очистится, плитка исчезнет с пульта.\n\nУдалить стартовую карточку: если есть следующая сцена по стрелке — метка переносится на неё; если нет — вся побочная линия удаляется с карты.\n\nВо время игры на пульте под блоком «Музыка» появляется «Побочные сюжетные линии» — плитки с превью и названием. Клик переносит партию на первую сцену линии. Программа запоминает, с какой сцены основного сюжета вы ушли.\n\nПока идёт побочная линия, в «Варианты ветвления» первой опцией всегда «Вернуться в основной сюжет» — возврат на запомненную сцену. История «Сюжетная линия» продолжает записывать все шаги, включая побочную ветку.\n\nЗапустить побочную линию из редактора нельзя — только с пульта во время сессии.',

    'help.section.sceneProps.title': 'Свойства сцены',
    'help.section.sceneProps.body':
      'Выберите сцену в списке слева — справа откроются её свойства.\n\n«Название сцены» — для мастера. «Описание» — заметки мастера с форматированием: рядом с подписью нажмите карандаш, откроется редактор (жирный, курсив, заголовки, списки, ссылки). Под подписью видно фрагмент текста или «описание отсутствует», если поле пустое. Во время сессии описание открывается с пульта в отдельном окне (см. «Пульт управления»), а не в блоке «Сюжетная линия».\n\nЕсли у сцены на карте есть карточка с синей меткой «ПОБОЧНАЯ», появится поле «Название побочной линии».\n\nКартинка или видео для игроков:\n\n1) В «Превью сцены» нажмите «Загрузить» или «Изменить».\n\n2) Выберите файл (PNG, JPG, WebP, GIF, видео и др.).\n\n3) Для картинки и видео можно «Повернуть» (шаг 90°). «Очистить» — убрать превью.\n\n4) Для картинки и видео можно включить «Затемнить сцену»: при показе игроки сначала увидят кадр в полной темноте, а вы снимете её с пульта «Кистью Открытия» (см. «Эффекты»).\n\n5) Кнопка «Редактор сцены» доступна и для картинки, и для видео — сетка, ловушки и неигровые токены поверх превью (см. «Редактор сцены», «Генератор сетки», «Ловушки», «Неигровые токены»).\n\nДля видео включите «Автостарт», если ролик должен сам начаться на экране игроков, и при необходимости «Цикл».\n\n«Аудио сцены» — музыка и звуки этого эпизода. «Загрузить» добавляет файлы. У каждого трека: «Авто» (играть при входе в сцену) и «Цикл». Корзина удаляет трек.\n\nСвязи между сценами задаются на карте, а не здесь — см. «Граф сцен».',

    'help.section.sceneEditor.title': 'Редактор сцены',
    'help.section.sceneEditor.body':
      '«Редактор сцены» — отдельное окно для подготовки карты: сетка боя, ловушки, неигровые токены и круглые токены НПС. Работает для сцен с изображением и с видео (оверлеи поверх ролика).\n\nОткрыть:\n\n1) Выберите сцену в списке слева.\n\n2) В «Свойствах сцены» загрузите картинку или видео, если превью ещё нет.\n\n3) Нажмите «Редактор сцены».\n\nСлева — аккордеоны «Сетка», «Неигровые токены», «НПС» и «Ловушки»; справа — карта сцены. Перетащите НПС на карту, чтобы добавить его круглый токен.\n\nНавигация по карте: колесо мыши — зум; средняя кнопка мыши или Space+ЛКМ — сдвиг вида. Delete / Backspace убирает выделенный маркер на карте.\n\nПод аккордеонами кнопка «Очистить сцену» убирает с текущей карты все ловушки и токены (пул токенов приложения не трогает).\n\nПодробнее: разделы «Генератор сетки», «Ловушки» и «Неигровые токены».',

    'help.section.grid.title': 'Генератор сетки',
    'help.section.grid.body':
      'Генератор сетки накладывает на превью сцены (картинку или видео) боевую сетку — квадратную или гексагональную. Сетка помогает ориентироваться по клеткам во время боя и видна и вам на пульте, и игрокам на презентации.\n\nНастроить:\n\n1) Откройте «Редактор сцены» (см. «Редактор сцены»).\n\n2) Раскройте аккордеон «Сетка» слева.\n\n3) Включите «Наложить сетку» — линии появятся поверх карты.\n\n4) «Тип» — «Квадратная» или «Гексогональная».\n\n5) «Цвет» — оттенок линий (удобно подобрать контраст к карте).\n\n6) «Размер» — ползунок ячейки: чем больше значение, тем крупнее клетки.\n\nПока сетка выключена, тип, цвет и размер недоступны для изменения, но запомненные значения сохраняются и вернутся при повторном включении.\n\nНастройки сетки хранятся в проекте вместе со сценой. Сетка рисуется под маркерами ловушек и токенов и не мешает их расставлять.\n\nВо время сессии на пульте можно включить «Привязка токенов к сетке»: при перетаскивании неигровые токены, токены НПС и игроков «прилипают» к клеткам (квадрат или гекс — по типу сетки). Если сетка выключена, галочка не действует.',

    'help.section.traps.title': 'Ловушки',
    'help.section.traps.body':
      'Ловушки — маркеры на карте сцены для скрытых угроз и сюрпризов. Расстановка хранится в проекте вместе со сценой; во время игры вы решаете, когда их показать игрокам.\n\nВ редакторе сцены:\n\n1) Откройте «Редактор сцены» для сцены с картинкой или видео (см. «Редактор сцены»).\n\n2) Раскройте аккордеон «Ловушки» слева.\n\n3) Перетащите тип из палитры на нужное место карты.\n\n4) Перетащите маркер, чтобы сдвинуть его; потяните за уголок выделенного маркера — изменить размер.\n\n5) Delete / Backspace — убрать выделенную ловушку. «Очистить сцену» снимает все маркеры сразу.\n\nТипы: Мимик, Взрыв, Яд, Пропасть, Стрела, Лазер и Метка (универсальный маркер без особого эффекта).\n\nВо время сессии:\n\n1) На пульте в «Предпросмотр экрана» видны все ловушки текущей сцены. Пока они скрыты от игроков, у вас маркеры остаются хорошо читаемыми (пунктирная рамка), чтобы их было удобно найти на карте.\n\n2) На презентации маркеры появляются только после проявления или срабатывания.\n\n3) Правый клик по маркеру на пульте:\n• «Проявить» — показать игрокам без срабатывания.\n• «Активировать» — проявить и запустить эффект: у Мимика, Пропасти, Стрелы и Лазера — анимация и звук; у Яда и Взрыва — как соответствующие эффекты с пульта (облако яда / взрыв); у Метки — короткая вспышка.\n• «Обезвредить» — показать как обезвреженную.\n\nСостояние ловушек (проявлены / сработали / обезврежены) сбрасывается при новом запуске сессии. Сами маркеры на карте остаются.',

    'help.section.tokens.title': 'Неигровые токены',
    'help.section.tokens.body':
      'Неигровые токены — картинки существ, предметов и маркеров, которые вы ставите на карту сцены. Библиотека токенов хранится в приложении на этом компьютере (не внутри файла проекта). На сцене сохраняется только расстановка: какой токен, где стоит, размер и поворот.\n\nВ редакторе сцены:\n\n1) Откройте «Редактор сцены» для сцены с картинкой или видео (см. «Редактор сцены»).\n\n2) Раскройте «Неигровые токены».\n\n3) «Добавить» — задайте уникальное название и изображение (кнопка выбора или перетаскивание файла).\n\n4) В поиске можно быстро найти токен по имени.\n\n5) Меню «⋮» у плитки — «Изменить» или «Удалить» (с подтверждением). Удаление из пула также убирает этот токен с текущей сцены.\n\n6) Перетащите плитку на карту, чтобы поставить токен. Выделите маркер: перетаскивание — сдвиг, уголок — размер, ручка поворота — угол. Delete / Backspace — убрать с карты. ПКМ по маркеру — меню: «Указать движение» / «Удалить» / «Сбросить на старт пути». «Очистить сцену» снимает все токены и ловушки со сцены.\n\nВо время сессии:\n\n1) На пульте в «Предпросмотр экрана» токены видны сразу (в отличие от ловушек их не нужно проявлять).\n\n2) Перетаскивайте токены левой кнопкой — новая позиция запоминается до конца текущей сессии, в том числе если вы возвращаетесь к сцене через «Сюжетную линию». При новом «Запустить» позиции снова берутся из редактора.\n\n3) Если на сцене включена сетка, на пульте можно отметить «Привязка токенов к сетке» — при перетаскивании токены встают по клеткам (см. «Генератор сетки»).\n\n4) На экране презентации токены только отображаются: клики и перетаскивание для игроков недоступны.\n\nПри экспорте и импорте сюжетных линий нужные файлы токенов упаковываются вместе с линией, чтобы на другом компьютере расстановка не «теряла» картинки.',

    'help.section.campaignAudio.title': 'Аудио игры',
    'help.section.campaignAudio.body':
      '«Аудио игры» в блоке «Свойства игры» — музыка всей кампании: тема, фон, атмосфера. Она не привязана к одной сцене.\n\n1) Нажмите «Загрузить» и выберите файлы.\n\n2) Для каждого трека отметьте «Авто» и «Цикл» по желанию.\n\n3) Удалить трек — иконка корзины.\n\nНа пульте музыка сцены важнее общей: пока играет трек сцены, кампанийная музыка приглушается. Когда у сцены нет своего звука или вы переключитесь вручную — общая музыка снова может играть.',

    'help.section.materials.title': 'Материалы',
    'help.section.materials.body':
      'Материалы — изображения кампании (карты, записки, чертежи), которые можно показать игрокам поверх сцены во время игры. Они общие для проекта и не привязаны к одной сцене.\n\nВ редакторе:\n\n1) В блоке «Свойства игры» нажмите «Материалы».\n\n2) «Добавить» — укажите уникальное название и изображение (PNG, JPG или WebP): кнопка выбора или перетаскивание файла.\n\n3) В списке можно искать, менять порядок перетаскиванием, править или удалять через меню «⋮» (перед удалением будет подтверждение).\n\n4) Под большим превью — «Повернуть»: поворот на 90° (учитывается и в плитке, и при показе на экране).\n\n5) Блок «Легенда» у выбранного материала: можно включить легенду, разместить нумерованные маркеры на картинке и подписать пункты списка. При показе на пульте и презентации легенда идёт вместе с материалом.\n\nВо время сессии:\n\n1) На пульте в «Инструменты» нажмите кнопку материалов (иконка карты сокровищ) — откроется отдельное окно со списком.\n\n2) Клик по плитке показывает материал поверх сцены на пульте и на презентации; повторный клик по той же плитке скрывает его. Можно открыть несколько материалов сразу — каждый кликом по своей плитке.\n\n3) На предпросмотре пульта материал можно перетаскивать, менять размер за углы и поворачивать; крестик закрывает показ этого материала (остальные остаются).\n\n4) В окне материалов лупы «+» / «−» — инструменты масштаба: выберите лупу, затем кликните по материалу в предпросмотре пульта.\n\nПри смене сцены показ материалов сбрасывается. Описание сцены и эффекты поля с материалами не связаны.',
    'help.section.players.title': 'Игроки',
    'help.section.players.body':
      'Раздел «Игроки» в шапке хранит локальную библиотеку живых игроков на этом компьютере (не внутри файла проекта).\n\n1) Откройте «Игроки» в шапке.\n\n2) «Добавить игрока» — имя и изображение обязательны. Пока идёт сохранение, видно окно с прогрессом.\n\n3) Справа — превью игрового токена: круг с цветной рамкой, аватар внутри (перетащите мышью, чтобы отцентровать; колесо мыши — увеличить/уменьшить), имя на тёмной подложке и выбор цвета рамки.\n\n4) Команды — плоские группы без вложенности: создайте команду, перетащите игрока в неё или в «Без команды».\n\nКампанийных НПС на сцену ставят отдельно: в «Редактор сцены» аккордеон «НПС» — плитки персонажей проекта, на карте они выглядят как такой же круглый токен.\n\nВо время сессии размер круглых токенов игроков и НПС на пульте можно менять ползунком «Размеры игр. токенов» (см. «Пульт управления»).',

    'help.section.npcs.title': 'НПС',
    'help.section.npcs.body':
      'НПС — персонажи кампании с аватаром, описанием и однонаправленными связями между собой. Они общие для проекта.\n\nВ редакторе:\n\n1) В блоке «Свойства игры» нажмите «НПС» — откроется отдельное окно редактора персонажей.\n\n2) «Добавить» — укажите уникальное имя и обязательный аватар (PNG, JPG или WebP): кнопка выбора или перетаскивание файла. При необходимости сразу заполните описание.\n\n3) Слева — список персонажей: поиск, порядок перетаскиванием; меню «⋮» — только удаление (с подтверждением; все связи с этим персонажем тоже удаляются). Персонажей можно объединять в группы (и подгруппы): создайте группу и перетащите НПС в неё или оставьте «Без группы». На графе связей доступен фильтр по группе.\n\n4) В центре — граф связей: протяните стрелку от одного персонажа к другому и укажите обязательное название связи.\n\n5) Справа — карточка выбранного персонажа: настройте круглый токен, цвет рамки, положение аватара (перетаскивание и колесо мыши для масштаба), имя, описание, группу и отношения. Поле «Тип» задаёт отношение: враждебный, нейтральный или дружественный — от этого зависит цвет кольца токена на карте.\n\n6) В «Редакторе сцены» раскройте «НПС» и перетащите персонажа на карту. Круглый токен можно двигать и менять его размер.\n\nВо время сессии на пульте токен НПС можно двигать; правый клик по маркеру:\n• если токен неактивен — «Сделать активным»;\n• если активен — «Открыть информацию» (карточка НПС), смена типа (враждебный / нейтральный / дружественный) и «Сделать неактивным».\nНа презентации токены только отображаются. Подробнее о пульте — в разделе «Пульт управления».',

    'help.section.session.title': 'Запуск сессии',
    'help.section.session.body':
      'Когда кампания готова, можно начать игру.\n\nОбычный запуск:\n\n1) На карте связей щёлкните правой кнопкой по карточке старта → «Начальная сцена».\n\n2) Нажмите «Запустить» в шапке редактора (левая часть кнопки).\n\nЗапуск с игроками: стрелка справа на кнопке «Запустить» → «Запустить с игроками» — выберите игроков и/или команды. Выбор сохранится на время сессии; на пульте появится «Показать игроков» / «Скрыть игроков». При смене сцены токены игроков скрываются.\n\nБыстрый запуск с любой карточки: правый клик по нужной карточке на карте → «Запустить с этой сцены». Презентация и пульт откроются сразу с выбранного места.\n\nОткроются «Презентация» (для игроков) и «Пульт управления» (для вас). Редактор на время показа затемняется — так и должно быть.\n\nВернуться к подготовке:\n\n1) На пульте нажмите «Выключить» или «Завершить показ» (если дальше некуда переходить).\n\n2) Дождитесь закрытия обоих окон.\n\nОкно «Презентация» перенесите на второй монитор, проектор или ТВ и разверните на весь экран (F11). Игроки увидят только картинку, видео и эффекты — без ваших кнопок.',

    'help.section.controlPanel.title': 'Пульт управления',
    'help.section.controlPanel.body':
      'Пульт — ваш стол во время игры. Игроки смотрят на презентацию, вы управляете всем отсюда.\n\nСлева сверху — «Инструменты», затем эффекты и ход сюжета; справа — мини-копия экрана игроков, варианты переходов и музыка.\n\nВ «Инструменты» кнопка с книгой открывает отдельное окно с оформленным описанием текущей сцены. Если описания нет, кнопка неактивна — при наведении подсказка «Описание отсутствует». Кнопка материалов (иконка карты) открывает окно со списком материалов кампании — подробнее в разделе «Материалы». Кнопка НПС (цветная иконка человека) открывает окно персонажей — см. раздел «НПС».\n\n«Предпросмотр экрана» показывает то же, что видят игроки. Здесь же рисуют эффекты — они сразу появляются на большом экране (и на картинке, и на видео). Если на сцене расставлены ловушки, они видны на предпросмотре: правый клик по маркеру — проявить, активировать или обезвредить (см. «Ловушки»). Неигровые токены, токены НПС и игроков тоже видны на предпросмотре: их можно двигать и менять размер за жёлтый уголок до конца сессии; ПКМ по токену НПС — активировать / открыть информацию / сменить тип / сделать неактивным (см. «НПС»). На видео-сценах внизу превью остаются кнопки воспроизведения и полоса перемотки.\n\nНад превью:\n• «Привязка токенов к сетке» — если на сцене включена сетка, перетаскиваемые токены (неигровые, НПС, игроки) встают по клеткам;\n• «Размеры игр. токенов» — общий масштаб круглых токенов игроков и НПС на предпросмотре и презентации;\n• «Показать игроков» / «Скрыть игроков» — после запуска с игроками.\n\n«Сюжетная линия» — цепочка пройденных эпизодов. Текущая сцена помечена «ТЕКУЩАЯ СЦЕНА». Клик по прошлому шагу переносит партию к тому месту на карте и добавляет новый шаг в историю.\n\n«Выключить демонстрацию» — закончить показ и вернуться в редактор.',

    'help.section.transitions.title': 'Переходы между сценами',
    'help.section.transitions.body':
      'Куда можно пойти дальше — видно на пульте в «Варианты ветвления». Это исходящие стрелки с текущей карточки на карте.\n\n1) Посмотрите карточки «ОПЦИЯ 1», «ОПЦИЯ 2» — там названия целевых сцен.\n\n2) Нажмите «Переключить» у нужного варианта.\n\n3) На презентации сменятся картинка и музыка, в сюжетной линии появится новый шаг.\n\nЕсли вариантов нет (конец ветки) — «Нет вариантов перехода». Нажмите «Завершить показ», чтобы закрыть окна.\n\nВарианты появляются только там, где вы провели стрелки на карте в редакторе.',

    'help.section.music.title': 'Музыка на пульте',
    'help.section.music.body':
      'Блок «Музыка» повторяет треки из редактора — отдельно «Музыка сцены» и «Музыка игры».\n\n▶ — воспроизвести, ⏸ — пауза, ⏹ — остановить. «Авто»/«Ручн.» и «Цикл»/«Один раз» показывают, как трек настроен в редакторе.\n\nКлик по полоске прогресса — перемотка (если известна длина). Стрелки ← → на полоске (когда она в фокусе) — на 5 секунд назад или вперёд.\n\nЕсли музыка не стартует сама — один раз нажмите ▶: после вашего действия звук обычно разрешается. Не играет файл — проверьте формат (MP3, WAV и т.п.).',

    'help.section.effects.title': 'Эффекты поля и действий',
    'help.section.effects.body':
      'Эффекты работают на сценах с картинкой и с видео. Рисуйте в «Предпросмотр экрана» — игроки увидят то же на презентации.\n\nВыберите инструмент слева:\n• Эффекты поля (туман, дождь, огонь, вода) — зажмите левую кнопку и ведите по карте.\n• Эффекты действий (молния, луч света, заморозка, тьма, облако яда, взрыв) — короткий клик или штрих; у некоторых есть звук.\n\nЕсли у сцены в свойствах включено «Затемнить сцену», появится блок «Управление затемнением» с «Кистью Открытия» 🔦 и «Кистью Закрытия» ⬛. «Кисть Открытия» снимает тьму на обоих экранах сразу; «Кисть Закрытия» снова накрывает уже открытые участки. У игроков нераскрытое остаётся чёрным, у вас на пульте — полузатемнённым. Состояние сохраняется, пока идёт показ и вы снова попадаете на ту же карточку сцены на карте. Это не то же самое, что эффект «Тьма» 🌑 в блоке действий.\n\nЛастик 🧹 — для эффектов поля (туман, дождь, огонь, вода) водите кистью, как «Кистью Открытия» для затемнения: стирается только пройденный участок. Эффекты действий (молния, луч и т.д.) убираются целиком при клике или проведении по ним. «Очистить эффекты» — снять всё сразу.\n\n«Радиус кисти» под панелью — чем больше число, тем шире мазок.',

    'help.section.presentation.title': 'Экран презентации',
    'help.section.presentation.body':
      '«Презентация» — то, что видят игроки: картинка или видео сцены (с учётом поворота из редактора) по вашим настройкам.\n\nЭффекты с пульта накладываются поверх. Меню и кнопки мастера здесь не показываются — клики по карте, ловушкам и токенам для игроков недоступны.\n\nЕсли у сцены включено «Затемнить сцену», игроки сначала видят полностью чёрный экран. Мастер открывает карту «Кистью Открытия» и при необходимости снова закрывает участки «Кистью Закрытия» на пульте.\n\nПри смене сцены с пульта картинка обновляется сама. Перенесите окно на экран для игроков и при необходимости спрячьте панель задач.\n\nПустой или тёмный экран — скорее всего, у сцены нет превью. Добавьте картинку в свойствах сцены в редакторе.',

    'help.section.importExport.title': 'Импорт и экспорт',
    'help.section.importExport.body':
      'Проект хранится как файл .ttrpg.zip — внутри уже все картинки, звуки и настройки.\n\nРезервная копия или перенос на другой ПК:\n\n1) «Проект» → «Экспорт».\n\n2) Выберите проект и куда сохранить.\n\n3) Скопируйте .ttrpg.zip на флешку, в облако или на другой компьютер.\n\nЗагрузить бэкап:\n\n1) «Проект» → «Импорт».\n\n2) Выберите .ttrpg.zip.\n\n3) Проект появится в списке на начальном экране.\n\nПри больших архивах показывается прогресс. Картинки и музыку отдельно переносить не нужно — всё внутри архива.',

    'help.section.settings.title': 'Настройки, язык и обновления',
    'help.section.settings.body':
      '«Настройки» → «Указать ключ» — ввести или сменить лицензию. «О лицензии» — проверить статус и срок.\n\n«Проверить обновления» (в установленной версии при активной лицензии) — если есть новая версия, можно скачать и перезапустить программу.\n\n«Язык» → «Русский» или «English» — меняет язык интерфейса. Выбор сохраняется между запусками.\n\nНомер версии — в шапке справа от меню. «О приложении» → «О программе» — о продукте и контактах поддержки.',

    'updates.dialogTitle': 'Обновления',
    'updates.checking': 'Проверка наличия обновлений…',
    'updates.available': 'Доступна новая версия {version}.',
    'updates.current': 'У вас установлена актуальная версия ({version}).',
    'updates.error': 'Не удалось проверить обновления: {message}',
    'updates.notPackaged': 'Проверка доступна только в установленной версии приложения.',
    'updates.noLicense': 'Нужна активная лицензия.',
    'updates.download': 'Обновить',
    'updates.downloading': 'Загрузка…',
    'updates.stageLine': 'Этап: {stage}',
    'updates.stage.checking': 'проверка обновлений',
    'updates.stage.available': 'доступна версия {version}',
    'updates.stage.not-available': 'актуальная версия',
    'updates.stage.downloading': 'загрузка{percent}',
    'updates.stage.installing': 'установка и перезапуск',
    'updates.stage.error': 'ошибка',
    'updates.stagePercent': ' ({percent}%)',

    'projectMenu.home': 'Начальный экран',
    'projectMenu.import': 'Импорт',
    'projectMenu.importFoundry': 'Импорт из Foundry',
    'projectMenu.export': 'Экспорт',
    'projectMenu.noProjects': 'Нет сохранённых проектов',

    'foundryImport.title': 'Импорт из Foundry',
    'foundryImport.hint':
      'Выберите папку или архив мира (.world) либо модуля Foundry VTT (версии 11+). Будет создан новый проект.',
    'foundryImport.sourceType': 'ТИП ИСТОЧНИКА',
    'foundryImport.folder': 'Папка',
    'foundryImport.archive': 'Архив (.zip / .fvtt)',
    'foundryImport.source': 'ИСТОЧНИК',
    'foundryImport.chooseFolder': 'Выбрать папку',
    'foundryImport.chooseArchive': 'Выбрать архив',
    'foundryImport.noSourceSelected': 'Не выбрано',
    'foundryImport.import': 'Импортировать',

    'fileMenu.rename': 'Переименовать проект',

    'scenes.search': 'Поиск сцен…',
    'scenes.new': '+ Новая сцена',
    'scenes.dropHint': 'Перетащите изображения или видео',
    'scenes.batchTitle': 'Создание сцен',
    'scenes.batchProgress': 'Сцена {current} из {total}',
    'scenes.dropSkippedTitle': 'Часть файлов не добавлена',
    'scenes.dropSkippedIntro': 'Эти файлы пропущены:',
    'scenes.dropSkippedUnsupported': 'неподдерживаемый формат',
    'scenes.dropSkippedNoPath': 'не удалось получить путь к файлу',
    'scenes.inspectorGame': 'Свойства игры',
    'scenes.inspectorScene': 'Свойства сцены',
    'scenes.projectLabel': 'Проект: {name}',
    'scenes.selectHint': 'Выберите сцену слева, чтобы редактировать её свойства.',
    'scenes.openProjectHint': 'Откройте проект, чтобы редактировать кампанию и сцены.',

    'rename.title': 'Переименовать проект',
    'rename.projectName': 'НАЗВАНИЕ ПРОЕКТА',
    'rename.projectPlaceholder': 'Название проекта…',
    'rename.projectMin': 'Минимум 3 символа.',
    'rename.projectDup': 'Проект с таким названием уже существует.',
    'rename.fileName': 'НАЗВАНИЕ ФАЙЛА ПРОЕКТА',
    'rename.fileInvalid': 'Минимум 3 символа, без символов <>:"/\\|?*',
    'rename.fileDup': 'Файл проекта с таким названием уже существует.',
    'rename.saving': 'Сохранение…',

    'export.title': 'Экспорт проекта',
    'export.project': 'ПРОЕКТ',
    'export.hint':
      'Выберите сюжетные линии для экспорта. В архив попадут выбранные линии, их сцены и материалы. Если в проекте есть НПС, на следующем шаге можно отметить, кого включить.',
    'export.npcsTitle': 'Экспорт НПС',
    'export.npcsHint':
      'Отметьте НПС для экспорта. Вместе с ними попадут связи между отмеченными и группы этих персонажей.',
    'export.selectAllNpcs': 'Отметить всех',
    'export.next': 'Далее',
    'export.back': 'Назад',
    'export.exporting': 'Экспорт…',
    'export.saveAs': 'Сохранить как…',

    'storyline.section': 'СЮЖЕТНАЯ ЛИНИЯ',
    'storyline.main': 'Основная линия',
    'storyline.loading': 'Загрузка линий…',
    'storyline.empty': 'В проекте нет сюжетных линий с метками «НАЧАЛО» или «ПОБОЧНАЯ».',
    'storyline.mainExistsHint': 'в проекте уже есть основная линия',

    'importSource.title': 'Импорт',
    'importSource.type': 'ТИП ИМПОРТА',
    'importSource.fromProject': 'Из проекта',
    'importSource.fromFile': 'Из файла',
    'importSource.project': 'ПРОЕКТ',
    'importSource.file': 'ФАЙЛ',
    'importSource.chooseFile': 'Выбрать файл',
    'importSource.noFileSelected': 'Файл не выбран',
    'importSource.noOtherProjects': 'Нет других проектов для импорта.',
    'importSource.fileOnlyHint': 'Выберите файл проекта (.ttrpg.zip) для полного импорта.',

    'importStoryline.title': 'Импорт сюжетных линий',
    'importStoryline.source': 'ИСТОЧНИК',
    'importStoryline.continue': 'Далее',
    'importStoryline.import': 'Импортировать',
    'importStoryline.conflictsTitle': 'Совпадение названий сцен',
    'importStoryline.conflictsHint':
      'В импортируемых линиях есть сцены с такими же названиями, как в текущем проекте. Выберите действие для каждой.',
    'importStoryline.createNewScene': 'Создать новую сцену',
    'importStoryline.useExistingScene': 'Использовать «{title}»',
    'importStoryline.reportTitle': 'Импорт завершён',
    'importStoryline.reportLines': 'Импортировано линий: {count}',
    'importStoryline.reportScenesCreated': 'Создано новых сцен: {count}',
    'importStoryline.reportScenesReused': 'Использовано существующих сцен: {count}',
    'importStoryline.reportNpcsCreated': 'Создано новых НПС: {count}',
    'importStoryline.reportNpcsReused': 'Использовано существующих НПС: {count}',
    'importStoryline.reportNodes': 'Добавлено карточек на граф: {count}',
    'importStoryline.reportEdges': 'Добавлено связей: {count}',
    'importStoryline.reportAssetsCopied': 'Скопировано файлов материалов: {count}',
    'importStoryline.reportAssetsReused': 'Повторно использовано материалов: {count}',
    'importStoryline.reportRenamedSides': 'Переименованы побочные линии: {names}',
    'importStoryline.npcConflictsTitle': 'Совпадение имён НПС',
    'importStoryline.npcConflictsHint':
      'В импортируемых линиях есть НПС с такими же именами, как в текущем проекте. Выберите действие для каждого.',
    'importStoryline.createNewNpc': 'Создать нового НПС',
    'importStoryline.useExistingNpc': 'Использовать «{name}»',

    'confirmDelete.title': 'Удаление проекта',
    'confirmDelete.body': 'Удалить проект «{name}» безвозвратно? Файл и кэш будут стёрты с диска.',
    'confirmDelete.failedTitle': 'Не удалось удалить',

    'confirmDeleteScene.title': 'Удаление сцены',
    'confirmDeleteScene.body': 'Удалить сцену «{name}»? Её нельзя будет восстановить.',

    'picker.title': 'Проекты',
    'picker.newPlaceholder': 'Название нового проекта…',
    'picker.create': 'Создать проект',
    'picker.search': 'Поиск кампаний…',
    'picker.searchEmpty': 'Ничего не найдено.',
    'picker.existing': 'СУЩЕСТВУЮЩИЕ',
    'picker.lockedHint':
      'Открытие и создание — после активации лицензии. Список показывает файлы в папке приложения.',
    'picker.empty': 'Пока нет проектов.',
    'picker.projectMenu': 'Меню проекта',
    'picker.openDisabled': 'Открытие проекта — после активации лицензии',
    'picker.opening': 'Открытие…',
    'picker.defaultName': 'Моя кампания',

    'campaign.label': 'АУДИО ИГРЫ',
    'campaign.noFiles': 'Файлов пока нет. Добавьте аудио.',
    'campaign.auto': 'Авто',
    'campaign.loop': 'Цикл',
    'campaign.removeTitle': 'Убрать из кампании',
    'campaign.upload': 'Загрузить',
    'drop.hintAudio': 'Перетащите аудиофайлы сюда',
    'drop.hintPreview': 'Перетащите изображение или видео',

    'materials.open': 'Материалы',
    'materials.managerTitle': 'Материалы',
    'materials.add': 'Добавить',
    'materials.addTitle': 'Новый материал',
    'materials.editTitle': 'Изменить материал',
    'materials.savingTitle': 'Сохранение материала',
    'materials.savingWait': 'Подождите…',
    'materials.savingProgress': 'Прогресс сохранения материала',
    'materials.edit': 'Изменить',
    'materials.search': 'Поиск материалов…',
    'materials.searchEmpty': 'Ничего не найдено.',
    'materials.empty': 'Материалов пока нет.',
    'materials.addPrompt': 'Добавьте материал',
    'materials.name': 'НАЗВАНИЕ',
    'materials.namePlaceholder': 'Название материала…',
    'materials.nameRequired': 'Укажите название.',
    'materials.nameDup': 'Материал с таким названием уже есть.',
    'materials.image': 'ИЗОБРАЖЕНИЕ',
    'materials.imageEmpty': 'Изображение не выбрано',
    'materials.imageRequired': 'Выберите изображение.',
    'materials.chooseImage': 'Выбрать изображение',
    'materials.dropHint': 'Перетащите изображение (PNG, JPG, WebP)',
    'materials.tileMenu': 'Меню материала',
    'materials.windowEmpty': 'Добавьте материалы в редакторе.',
    'materials.closeOverlay': 'Закрыть материалы',
    'materials.rotateOverlay': 'Повернуть',
    'materials.deleteTitle': 'Удаление материала',
    'materials.deleteConfirm': 'Вы уверены, что хотите удалить материал «{name}»?',
    'materials.zoomIn': 'Увеличить',
    'materials.zoomOut': 'Уменьшить',
    'materials.zoomInHint': 'Кликните по материалу в предпросмотре пульта, чтобы увеличить.',
    'materials.zoomOutHint': 'Кликните по материалу в предпросмотре пульта, чтобы уменьшить.',
    'materials.zoomIdleHint': 'Выберите лупу, затем кликните по материалу в предпросмотре пульта.',

    'players.managerTitle': 'Игроки',
    'players.add': 'Добавить игрока',
    'players.search': 'Поиск игроков…',
    'players.selectPrompt': 'Выберите игрока или добавьте нового.',
    'players.name': 'ИМЯ',
    'players.namePlaceholder': 'Имя игрока…',
    'players.ringColor': 'ЦВЕТ РАМКИ',
    'players.chooseImage': 'Выбрать изображение',
    'players.dropHint': 'Перетащите изображение (PNG, JPG, WebP)',
    'players.savingTitle': 'Сохранение игрока',
    'players.savingWait': 'Подождите…',
    'players.ungrouped': 'Без команды',
    'players.addTeam': 'Новая команда',
    'players.teamName': 'Название команды',
    'players.teamColor': 'Цвет команды',
    'players.teamMenu': 'Меню команды',
    'players.dropHere': 'Перетащите игрока сюда',
    'players.deleteTitle': 'Удаление игрока',
    'players.deleteConfirm': 'Вы уверены, что хотите удалить игрока «{name}»?',
    'players.deleteTeamTitle': 'Удаление команды',
    'players.deleteTeamConfirm': 'Вы уверены, что хотите удалить команду «{name}»? Игроки останутся без команды.',

    'npcs.open': 'НПС',
    'npcs.editorTitle': 'НПС',
    'npcs.add': 'Добавить',
    'npcs.addTitle': 'Новый НПС',
    'npcs.editTitle': 'Изменить НПС',
    'npcs.savingTitle': 'Сохранение НПС',
    'npcs.savingWait': 'Подождите…',
    'npcs.savingProgress': 'Прогресс сохранения НПС',
    'npcs.graphLoading': 'Загрузка графа…',
    'npcs.edit': 'Изменить',
    'npcs.tileMenu': 'Меню НПС',
    'npcs.search': 'Поиск НПС…',
    'npcs.searchEmpty': 'Ничего не найдено.',
    'npcs.empty': 'НПС пока нет.',
    'npcs.selectPrompt': 'Выберите НПС в списке или на графе.',
    'npcs.name': 'ИМЯ',
    'npcs.namePlaceholder': 'Имя персонажа…',
    'npcs.nameRequired': 'Укажите имя.',
    'npcs.nameDup': 'НПС с таким именем уже есть.',
    'npcs.avatar': 'АВАТАР',
    'npcs.avatarEmpty': 'Аватар не выбран',
    'npcs.avatarRequired': 'Выберите аватар.',
    'npcs.chooseAvatar': 'Выбрать аватар',
    'npcs.dropHint': 'Перетащите изображение (PNG, JPG, WebP)',
    'npcs.disposition': 'ТИП',
    'npcs.disposition.hostile': 'Враждебный',
    'npcs.disposition.neutral': 'Нейтральный',
    'npcs.disposition.friendly': 'Дружественный',
    'npcs.makeHostile': 'Сделать враждебным',
    'npcs.makeNeutral': 'Сделать нейтральным',
    'npcs.makeFriendly': 'Сделать дружественным',
    'npcs.inactive': 'Неактивен',
    'npcs.makeActive': 'Сделать активным',
    'npcs.openInfo': 'Открыть информацию',
    'npcs.ringColor': 'ЦВЕТ РАМКИ ТОКЕНА',
    'npcs.description': 'ОПИСАНИЕ',
    'npcs.descriptionPlaceholder': 'Описание персонажа…',
    'npcs.descriptionEmpty': 'Описание отсутствует',
    'npcs.relations': 'Отношения',
    'npcs.untitled': 'Без имени',
    'npcs.deleteTitle': 'Удаление НПС',
    'npcs.deleteConfirm': 'Вы уверены, что хотите удалить НПС «{name}»? Все связи с ним будут удалены.',
    'npcs.relationCreateTitle': 'Название связи',
    'npcs.relationEditTitle': 'Название связи',
    'npcs.relationLabel': 'НАЗВАНИЕ',
    'npcs.relationLabelPlaceholder': 'Например: друзья, враги…',
    'npcs.relationLabelRequired': 'Укажите название связи.',
    'npcs.relationEdit': 'Редактировать',
    'npcs.relationDelete': 'Удалить',
    'npcs.relationDeleteTitle': 'Удаление связи',
    'npcs.relationDeleteConfirm': 'Удалить связь «{name}»?',
    'npcs.graphZoomBar': 'Масштаб графа',
    'npcs.graphZoomIn': 'Увеличить',
    'npcs.graphZoomOut': 'Уменьшить',
    'npcs.graphFitAll': 'Показать всё',
    'npcs.windowEmpty': 'Добавьте НПС в редакторе.',
    'npcs.selectToShow': 'Выберите персонажа в списке — он появится на экране.',
    'npcs.closeOverlay': 'Закрыть всех',
    'npcs.rotateOverlay': 'Повернуть',
    'npcs.zoomIn': 'Увеличить',
    'npcs.zoomOut': 'Уменьшить',
    'npcs.zoomInHint': 'Кликните по аватару в предпросмотре пульта, чтобы увеличить.',
    'npcs.zoomOutHint': 'Кликните по аватару в предпросмотре пульта, чтобы уменьшить.',
    'npcs.zoomIdleHint': 'Выберите лупу, затем кликните по аватару в предпросмотре пульта.',
    'npcs.addToMap': 'На карту',
    'npcs.addToMapHint': 'Поставить токен на текущую сцену до конца сессии. Можно несколько раз.',
    'npcs.addToMapNoScene': 'Нет активной сцены — сначала запустите игру.',
    'npcs.dragToMapHint':
      'Перетащите плитку на предпросмотр пульта или нажмите «На карту». Токен только на сессию.',
    'npcs.removeFromMap': 'Убрать с карты',
    'npcs.ungrouped': 'Без группы',
    'npcs.addGroup': 'Новая группа',
    'npcs.editGroup': 'Изменить группу',
    'npcs.deleteGroup': 'Удалить группу',
    'npcs.groupName': 'Название группы',
    'npcs.groupColor': 'Цвет',
    'npcs.groupNameRequired': 'Укажите название группы.',
    'npcs.groupNameDup': 'Группа с таким названием уже есть.',
    'npcs.deleteGroupTitle': 'Удаление группы',
    'npcs.deleteGroupConfirm':
      'Удалить группу «{name}»? НПС станут без группы, вложенные группы будут подняты на уровень выше.',
    'npcs.addSubgroup': 'Добавить подгруппу',
    'npcs.group': 'ГРУППА',
    'npcs.graphFilterAll': 'Все',
    'npcs.graphFilterUngrouped': 'Без группы',
    'npcs.graphFilter': 'Фильтр графа',

    'scene.title': 'НАЗВАНИЕ СЦЕНЫ',
    'scene.description': 'ОПИСАНИЕ',
    'scene.descriptionEmpty': 'описание отсутствует',
    'scene.descriptionModalTitle': 'Описание сцены',
    'scene.descriptionPlaceholder': 'Введите описание сцены…',
    'scene.descriptionToolbar': 'Форматирование',
    'scene.descriptionBold': 'Жирный',
    'scene.descriptionItalic': 'Курсив',
    'scene.descriptionUnderline': 'Подчёркнутый',
    'scene.descriptionHeading2': 'Заголовок',
    'scene.descriptionHeading3': 'Подзаголовок',
    'scene.descriptionQuote': 'Цитата',
    'scene.descriptionBulletList': 'Маркированный список',
    'scene.descriptionOrderedList': 'Нумерованный список',
    'scene.descriptionLink': 'Ссылка',
    'scene.descriptionLinkPrompt': 'URL ссылки',
    'scene.preview': 'ПРЕВЬЮ СЦЕНЫ',
    'scene.previewHint': 'Файл изображения (PNG, JPG, WebP, GIF и т.д.).',
    'scene.previewEmpty': 'Превью не задано',
    'scene.previewBusy': 'Загрузка и оптимизация изображения…',
    'scene.previewBusySelecting': 'Выберите файл…',
    'scene.previewOptimizing': 'Превью уже доступно. Оптимизируем в фоне…',
    'scene.previewReady': 'Превью готово',
    'scene.previewFailed': 'Превью добавлено, но оптимизация не удалась',
    'scene.change': 'Изменить',
    'scene.clear': 'Очистить',
    'scene.autostart': 'Автостарт',
    'scene.darkenScene': 'Затемнить сцену',
    'scene.rotate': 'Повернуть',
    'scene.audio': 'АУДИО СЦЕНЫ',
    'scene.removeTitle': 'Убрать из сцены',
    'scene.branching': 'ВЕТВЛЕНИЯ',
    'scene.branchingHint':
      'Перетащите сцену из списка на граф. С одной карточки можно задать несколько вариантов — по одной связи на каждую целевую сцену. Повторно к той же сцене (включая вторую карточку той же сцены на графе) подключить нельзя.',

    'sceneCard.current': 'ТЕКУЩАЯ',
    'sceneCard.menu': 'Меню сцены',

    'graph.badgeStart': 'НАЧАЛО',
    'graph.badgeSideStory': 'ПОБОЧНАЯ',
    'graph.untitled': 'Без названия',
    'graph.videoBadge': 'Видео',
    'graph.audioBadge': 'Аудио',
    'graph.loop': 'Цикл',
    'graph.autoplay': 'Автостарт',
    'graph.previewAutostart': 'Авто превью',
    'graph.videoLoop': 'Цикл видео',
    'graph.zoomBar': 'Масштаб графа',
    'graph.zoomIn': 'Увеличить',
    'graph.zoomOut': 'Уменьшить',
    'graph.fitAll': 'Показать всё',
    'graph.startScene': 'Начальная сцена',
    'graph.unsetStartScene': 'Снять метку «Начальная сцена»',
    'graph.sideStoryStartScene': 'Начальная сцена побочной линии',
    'graph.unsetSideStoryStartScene': 'Снять метку «Начальная сцена побочной линии»',
    'graph.runFromScene': 'Запустить с этой сцены',

    'scene.sideStoryLineTitle': 'Название побочной линии',

    'control.remoteTitle': 'ПУЛЬТ УПРАВЛЕНИЯ',
    'control.instruments': 'ИНСТРУМЕНТЫ',
    'control.descriptionTool': 'Описание',
    'control.materialsTool': 'Материалы',
    'control.npcsTool': 'НПС',
    'control.descriptionMissing': 'Описание отсутствует',
    'control.effects': 'ЭФФЕКТЫ',
    'control.tools': 'Очистка',
    'control.fieldEffects': 'Эффекты поля',
    'control.actionEffects': 'Эффекты действий',
    'control.eraser': 'Ластик',
    'control.clearEffects': 'Очистить эффекты',
    'control.fog': 'Туман',
    'control.rain': 'Дождь',
    'control.fire': 'Огонь',
    'control.water': 'Вода',
    'control.darkness': 'Тьма',
    'control.darknessControl': 'Управление затемнением',
    'control.explorerBrush': 'Кисть Открытия',
    'control.closerBrush': 'Кисть Закрытия',
    'control.lightning': 'Молния',
    'control.sunbeam': 'Луч света',
    'control.freeze': 'Заморозка',
    'control.poisonCloud': 'Облако яда',
    'control.explosion': 'Взрыв',
    'control.brushRadius': 'Радиус кисти',
    'control.effectsSound': 'Звук эффектов',
    'control.storyLine': 'СЮЖЕТНАЯ ЛИНИЯ',
    'control.gotoScene': 'Перейти к этой сцене',
    'control.currentSceneBadge': 'ТЕКУЩАЯ СЦЕНА',
    'control.passed': 'Пройдено',
    'control.noActiveScene': 'Нет активной сцены.',
    'control.screenPreview': 'Предпросмотр экрана',
    'control.npcTokenScale': 'Размеры игр. токенов',
    'control.resizeToken': 'Изменить размер',
    'control.snapTokensToGrid': 'Привязка токенов к сетке',
    'control.snapTokensToGridNoGrid': 'Сетка на текущей сцене выключена — привязка не применяется',
    'control.stopPresentation': 'Выключить',
    'control.showPlayers': 'Показать игроков',
    'control.hidePlayers': 'Скрыть игроков',
    'control.videoBrushHint':
      'Видео-превью: кисть эффектов отключена (как на экране демонстрации — оверлей только для изображения).',
    'control.branches': 'Варианты ветвления',
    'control.returnToMainStory': 'Вернуться в основной сюжет',
    'control.sideStoryLines': 'Побочные сюжетные линии',
    'control.option': 'ОПЦИЯ {n}',
    'control.unnamed': 'Без названия',
    'control.switchScene': 'Переключить',
    'control.noBranches': 'Нет вариантов перехода.',
    'control.endPresentation': 'Завершить показ',
    'control.music': 'Музыка',
    'control.sceneMusic': 'МУЗЫКА СЦЕНЫ',
    'control.gameMusic': 'МУЗЫКА ИГРЫ',
    'control.noSceneAudio': 'В текущей сцене нет аудио.',
    'control.noGameAudio': 'В игре нет аудио.',
    'control.modeAuto': 'Авто',
    'control.modeManual': 'Ручн.',
    'control.once': 'Один раз',
    'control.loop': 'Цикл',
    'control.scrubSeek': 'Клик — перемотка',
    'control.durationUnknown': 'Длительность неизвестна',
    'control.pauseSceneMusic': 'Пауза (сцена)',
    'control.pauseSceneMusicTitle': 'В сцене есть музыка',
    'control.pauseCampaignTitle': 'Пауза: в сцене есть музыка',
    'control.playFailed': 'Не удалось запустить.',
    'control.audioAutoplayBlocked':
      'Автозапуск заблокирован (нужно действие пользователя) или ошибка воспроизведения.',
    'control.audioNoUrl': 'URL не получен',
    'control.audioNoUrlDetail': 'Не удалось получить dnd://asset URL для аудио.',
    'control.audioBlocked': 'Ошибка/блок',
    'control.audioError': 'Ошибка',
    'control.audioMediaError': 'MediaError code={code} (1=ABORTED, 2=NETWORK, 3=DECODE, 4=SRC_NOT_SUPPORTED)',
    'control.audioLoading': 'Загрузка…',
    'control.audioPlaying': 'Играет',
    'control.audioPaused': 'Пауза',
    'control.audioStopped': 'Остановлено',
    'control.previewTrackLabel': 'Превью без субтитров',
    'control.transportPlay': 'Воспроизведение',
    'control.transportPause': 'Пауза',
    'control.transportStop': 'Стоп',
    'control.volume': 'Громкость',
  },
  en: {
    'common.close': 'Close',
    'common.cancel': 'Cancel',
    'common.save': 'Save',
    'common.saving': 'Saving…',
    'common.edit': 'Edit',
    'common.understood': 'OK',
    'common.message': 'Message',
    'common.error': 'Error',
    'common.delete': 'Delete',
    'common.closeMenu': 'Close menu',

    'app.brandTitle': 'TTRPG Player',

    'notice.campaignAudioEmpty': 'No audio was added. Check the file format.',

    'license.checkingTitle': 'Checking license…',
    'license.checkingWait': 'Please wait.',
    'license.requiredTitle': 'License required',
    'license.requiredHint':
      'Enter your key via Settings → Enter license key. Until activation, only Settings is available.',
    'license.tokenTitle': 'Enter license key',
    'license.tokenKey': 'KEY',
    'license.tokenPlaceholder': 'TTRPG- or DND- product key…',
    'license.tokenSaving': 'Saving…',
    'license.eulaTitle': 'End User License Agreement',
    'license.eulaReject': 'Decline',
    'license.eulaAccept': 'I accept the terms',
    'license.eulaNoteEn':
      'The binding legal text below is in Russian. If you need an English summary, contact support.',
    'license.aboutTitle': 'About license',
    'license.aboutDevSkip': 'Development mode: license checks are disabled (DND_SKIP_LICENSE).',
    'license.aboutStatus': 'STATUS',
    'license.aboutProduct': 'PRODUCT',
    'license.aboutLicenseId': 'LICENSE ID',
    'license.aboutExpiry': 'EXPIRES',
    'license.aboutDevice': 'DEVICE',
    'license.aboutNoData': 'No license data.',
    'license.reason.ok': 'Active',
    'license.reason.none': 'No key provided',
    'license.reason.expired': 'Expired',
    'license.reason.bad_signature': 'Invalid signature',
    'license.reason.bad_payload': 'Invalid token format',
    'license.reason.malformed': 'Malformed token',
    'license.reason.not_yet_valid': 'Not yet valid',
    'license.reason.wrong_device': 'Wrong bound device',
    'license.reason.revoked_remote': 'Revoked on server',

    'presentation.overlay': 'Presentation running',
    'presentation.title': 'Presentation running',
    'presentation.body': 'The editor is locked. Close the Presentation and Control windows to continue.',

    'zip.progress': 'Operation progress',
    'zip.importTitle': 'Import project',
    'zip.exportTitle': 'Export project',

    'top.settings': 'Settings',
    'top.project': 'Project',
    'top.file': 'File',
    'top.players': 'Players',
    'top.backToProjects': 'Back to projects',
    'top.appVersion': 'App version',
    'top.run': 'Run',
    'top.runMenuAria': 'More launch options',
    'top.runWithPlayers': 'Run with players',
    'top.runWithPlayersTitle': 'Launch with players',
    'top.runWithPlayersHint': 'Select players and/or teams. The selection is kept for this session.',
    'top.runWithPlayersTeams': 'Teams',
    'top.runWithPlayersPlayers': 'Players',
    'top.runWithPlayersEmpty': 'No players in the library.',
    'top.runWithPlayersConfirm': 'Launch',
    'top.launching': 'Starting…',
    'top.afterLicense': 'Available after license activation',
    'top.setStartScene': 'Set a start scene on the graph (right‑click a node)',
    'top.runHelpAria': 'How to enable the Run button',

    'menu.enterKey': 'Enter license key',
    'menu.aboutLicense': 'About license',
    'menu.checkUpdates': 'Check for updates',
    'menu.language': 'Language',
    'menu.langRu': 'Русский',
    'menu.langEn': 'English',
    'menu.aboutProgram': 'About',
    'menu.instructions': 'Instructions',

    'top.aboutApp': 'About',

    'app.about.title': 'About',
    'app.about.tagline': 'Campaign editor and GM control panel for tabletop RPGs',
    'app.about.description':
      'TTRPG Player is a desktop app for game masters: build your campaign as a scene graph with maps, music and branching paths, then run the session from the control panel. Players see only the presentation on a second screen — no editor panels.',
    'app.about.versionLabel': 'VERSION',
    'app.about.developerLabel': 'DEVELOPER',
    'app.about.developer':
      'Independent development. For licensing, purchase and support questions, use the email below.',
    'app.about.supportLabel': 'SUPPORT',
    'app.about.supportEmail': 'player.ttrpg@gmail.com',
    'app.about.websiteLabel': 'WEBSITE & UPDATES',
    'app.about.websiteUrl': 'https://ttrpgplayer.ru/',

    'help.title': 'Instructions',
    'help.navAria': 'Instruction sections',

    'help.section.overview.title': 'App overview',
    'help.section.overview.body':
      'When you launch the app, you land in the Editor — where you prepare your campaign: scenes, images, music, and how episodes connect. When it is time to play, click Run. That opens Presentation for your players and the Control panel for you.\n\nIn the editor, the left column lists scenes, the center shows the story map, and the right column holds game and scene settings.\n\nWhile a session is running, the editor is temporarily locked — that is normal. Close presentation and the control panel to edit again.\n\nInternet is only needed for license activation and update checks. All projects and files stay on your computer.',

    'help.section.license.title': 'License and first launch',
    'help.section.license.body':
      'Before you can work with projects, activate your license once.\n\n1) Open Settings → Enter license key.\n\n2) If you see the license agreement, read and accept it.\n\n3) Paste the key from your email (TTRPG-… or legacy DND-…) and click Save.\n\nUntil activation succeeds, only Settings is available. After that, projects, scenes, and running a session unlock.\n\nStatus, expiry, and device binding are under Settings → About license. The key is tied to this PC; another computer may need a separate activation per your purchase terms.',

    'help.section.projects.title': 'Projects',
    'help.section.projects.body':
      'A project is your whole campaign: scenes, media, and connections between them.\n\nCreate a new campaign:\n\n1) On the home screen, type a name in the field on the left.\n\n2) Click Create project.\n\nOpen an existing one — click its name in the list. Return to the list: Project → Home, or click the app title in the header.\n\nMove a campaign to another computer:\n\n1) Project → Export — save a copy as .ttrpg.zip.\n\n2) On the other PC — Project → Import and pick that file.\n\nRename an open project: File → Rename project (at least 3 characters; file names cannot contain <>:"/\\|?*).\n\nTo delete a project from disk:\n\n1) On the project card, click ⋮.\n\n2) In the menu, choose Delete.\n\n3) Confirm deletion in the dialog.\n\nThe project file and cache are then removed permanently. If you might need the campaign again, export a backup first.',

    'help.section.scenes.title': 'Scenes',
    'help.section.scenes.body':
      'A scene is one episode: a location, story beat, or dialogue. It has a title, an image or video for players, notes for the GM, and its own music.\n\nAdd a scene:\n\n1) In the left column, click + New scene.\n\n2) Set the title and adjust properties on the right (see Scene properties).\n\nSearch scenes… helps you find one quickly. Click a card to select it — it will also highlight on the story map.\n\nDelete: right-click a list card → Delete. The scene disappears from the list and map, including all links.\n\nDrag a scene from the list onto the map to place it as a node (see Scene graph).',

    'help.section.graph.title': 'Scene graph',
    'help.section.graph.body':
      'The map in the center shows how episodes connect. Each box is a spot on your story; the same scene can appear more than once (for example, players return to the tavern).\n\nPlace a scene on the map:\n\n1) Grab a scene in the left list.\n\n2) Drag it onto empty space on the map.\n\nConnect two scenes:\n\n1) Point at the bottom dot on the first card.\n\n2) Drag a line to the top dot on the second and release — an arrow appears.\n\nOne scene can have several outgoing arrows — that is how you branch the story. You cannot draw a second arrow between the same pair.\n\nRemove an arrow: right-click the line → Delete. A normal click on the line does nothing.\n\nSet where the game starts: right-click a card → Start scene (a START badge appears), then click Run in the header. You can also start from any main-story card: right-click → Start from this scene — presentation and the control panel open at that spot (no START badge required). Side-story cards do not offer this menu item.\n\nRemove a card from the map without deleting the scene from the list: right-click → Delete.\n\nZoom with the buttons at the bottom or the mouse wheel. Fit view shows the whole map.',

    'help.section.sideStorylines.title': 'Side storylines',
    'help.section.sideStorylines.body':
      'A side storyline is a separate branch not connected to the main plot — for detours, flashbacks, side quests, and scenes off the main path.\n\nCreate one in the editor:\n\n1) Place scenes on the map and link them in an isolated group — it must not touch the main story (purple links) or other side storylines.\n\n2) Right-click the starting card → Side storyline start scene. A blue SIDE badge appears.\n\n3) In scene properties, set Side storyline title — it appears on the control panel.\n\nThe menu item is hidden if the card is already linked to the main story (purple START anywhere in the group) or another side storyline (blue SIDE in the group).\n\nLinks inside a side storyline and card selection use blue (#0078d4). You cannot link main to side, or one side storyline to another.\n\nClear the mark: right-click → Clear side storyline start mark. The title is cleared and the tile disappears from the control panel.\n\nDeleting the start card: if there is a next scene along an arrow, the mark moves to it; otherwise the whole side storyline is removed from the map.\n\nDuring play, Side storylines appears under Music on the control panel — tiles with preview and title. Clicking jumps to the first scene. The app remembers which main-story scene you left from.\n\nWhile in a side storyline, Branch options always lists Return to main story first — back to the remembered scene. Storyline history keeps recording all steps, including inside side branches.\n\nYou cannot launch a side storyline from the editor — only from the control panel during a session.',

    'help.section.sceneProps.title': 'Scene properties',
    'help.section.sceneProps.body':
      'Select a scene in the left list — its properties open on the right.\n\nScene title is for the GM. Description is GM notes with formatting: click the pencil next to the label to open the editor (bold, italic, headings, lists, links). Below the label you see a text preview, or “no description” when empty. During a session, open the description from the control panel in a separate window (see Control panel) — it is not shown inside the Storyline list.\n\nIf the scene has a graph card with a blue SIDE badge, a Side storyline title field appears.\n\nImage or video for players:\n\n1) Under Scene preview, click Upload or Change.\n\n2) Pick a file (PNG, JPG, WebP, GIF, video, etc.).\n\n3) For images and video, use Rotate (90° steps). Clear removes the preview.\n\n4) For images and video, enable Darken scene so players start in full darkness and you reveal the frame with the Opening brush on the control panel (see Effects).\n\n5) Scene editor works for both images and video — battle grid, traps, and non-player tokens over the preview (see Scene editor, Grid generator, Traps, and Non-player tokens).\n\nFor video, enable Autostart if the clip should start on its own on the player screen, and Loop if needed.\n\nScene audio — music and sounds for this episode. Upload adds files. Per track: Auto (play when entering the scene) and Loop. The trash icon removes a track.\n\nLinks between scenes are drawn on the map, not here — see Scene graph.',

    'help.section.sceneEditor.title': 'Scene editor',
    'help.section.sceneEditor.body':
      'Scene editor is a separate window for preparing the map: battle grid, traps, non-player tokens, and circular NPC tokens. It works for image and video scenes (overlays sit on top of the clip).\n\nOpen it:\n\n1) Select a scene in the left list.\n\n2) In Scene properties, upload an image or video if the scene has none yet.\n\n3) Click Scene editor.\n\nOn the left are the Grid, Non-player tokens, NPCs, and Traps accordions; on the right is the scene map. Drag an NPC onto the map to add its circular token.\n\nMap navigation: mouse wheel zooms; middle mouse button or Space+left-drag pans the view. Delete / Backspace removes the selected marker on the map.\n\nUnder the accordions, Clear scene removes every trap and token from the current map (it does not delete tokens from the app library).\n\nFor details, see Grid generator, Traps, and Non-player tokens.',

    'help.section.grid.title': 'Grid generator',
    'help.section.grid.body':
      'The grid generator overlays a battle grid on the scene preview (image or video) — square or hexagonal. It helps track cells in combat and is visible both on your control panel and on the players’ presentation.\n\nSet it up:\n\n1) Open Scene editor (see Scene editor).\n\n2) Expand the Grid accordion on the left.\n\n3) Enable Overlay grid — lines appear over the map.\n\n4) Type — Square or Hexagonal.\n\n5) Color — line tint (pick contrast that suits the map).\n\n6) Size — cell size slider: higher values mean larger cells.\n\nWhile the grid is off, type, color, and size stay disabled, but the saved values return when you turn it back on.\n\nGrid settings are stored with the scene in the project. The grid draws under trap and token markers and does not block placing them.\n\nDuring a session you can enable Snap tokens to grid on the control panel: dragging non-player tokens, NPC tokens, and player tokens snaps them to cells (square or hex, matching the grid type). If the grid is off, the checkbox has no effect.',

    'help.section.traps.title': 'Traps',
    'help.section.traps.body':
      'Traps are markers on the scene map for hidden threats and surprises. Placement is stored with the scene in the project; during play you decide when players see them.\n\nIn the scene editor:\n\n1) Open Scene editor for an image or video scene (see Scene editor).\n\n2) Expand the Traps accordion on the left.\n\n3) Drag a type from the palette onto the map.\n\n4) Drag a marker to move it; drag the corner handle of the selected marker to resize.\n\n5) Delete / Backspace removes the selected trap. Clear scene removes all markers at once.\n\nTypes: Mimic, Explosion, Poison, Pit, Arrow, Laser, and Marker (a generic marker without a special effect).\n\nDuring a session:\n\n1) On the control panel Screen preview you see every trap on the current scene. While still hidden from players, markers stay clearly readable on your side (dashed outline) so you can find them on the map.\n\n2) On presentation, markers appear only after reveal or activation.\n\n3) Right-click a marker on the control panel:\n• Reveal — show it to players without triggering.\n• Activate — reveal and play the effect: Mimic, Pit, Arrow, and Laser play animation and sound; Poison and Explosion use the matching control-panel effects (poison cloud / explosion); Marker shows a short flash.\n• Disarm — show it as disarmed.\n\nTrap runtime state (revealed / triggered / disarmed) resets when you start a new session. Markers placed on the map remain.',

    'help.section.tokens.title': 'Non-player tokens',
    'help.section.tokens.body':
      'Non-player tokens are images of creatures, props, and markers you place on the scene map. The token library lives in the app on this computer (not inside the project file). The scene only stores placements: which token, where it stands, size, and rotation.\n\nIn the scene editor:\n\n1) Open Scene editor for an image or video scene (see Scene editor).\n\n2) Expand Non-player tokens.\n\n3) Add — enter a unique name and an image (choose file or drop one).\n\n4) Use search to find a token by name.\n\n5) The ⋮ menu on a tile opens Edit or Delete (with confirmation). Deleting from the library also removes that token from the current scene.\n\n6) Drag a tile onto the map to place it. Select a marker: drag to move, corner handle to resize, rotate handle to turn. Delete / Backspace removes it from the map. Right-click opens a menu: Set movement / Delete / Reset to path start. Clear scene removes all tokens and traps from the scene.\n\nDuring a session:\n\n1) On the control panel Screen preview, tokens are visible right away (unlike traps, they do not need revealing).\n\n2) Drag tokens with the left button — the new position is kept until the current session ends, including when you return to the scene via Storyline. A new Run resets positions to what you set in the editor.\n\n3) If the scene has a grid, enable Snap tokens to grid on the control panel so dragged tokens snap to cells (see Grid generator).\n\n4) On the presentation screen tokens are display-only: players cannot click or drag them.\n\nWhen you export or import storylines, the needed token files are packed with the line so placements keep their images on another computer.',

    'help.section.campaignAudio.title': 'Game audio',
    'help.section.campaignAudio.body':
      'Game audio under Game properties is music for the whole campaign: theme, ambience, background. It is not tied to one scene.\n\n1) Click Upload and choose files.\n\n2) Set Auto and Loop per track as you like.\n\n3) Remove a track with the trash icon.\n\nOn the control panel, scene music comes first: while a scene track plays, campaign music pauses. When the scene has no track or you take manual control, campaign music can play again.',

    'help.section.materials.title': 'Materials',
    'help.section.materials.body':
      'Materials are campaign images (maps, notes, sketches) you can show players on top of the scene during play. They belong to the project and are not tied to one scene.\n\nIn the editor:\n\n1) Under Game properties, click Materials.\n\n2) Add — enter a unique name and an image (PNG, JPG, or WebP) via Choose image or by dropping a file.\n\n3) In the list you can search, reorder by drag-and-drop, and edit or delete via the ⋮ menu (delete asks for confirmation).\n\n4) Under the large preview, Rotate turns the image by 90° (applied in the tile and when shown on screen).\n\n5) The Legend block for the selected material: enable the legend, place numbered markers on the image, and label the list items. When shown on the control panel and presentation, the legend travels with the material.\n\nDuring a session:\n\n1) On the control panel under Tools, click the materials button (treasure-map icon) to open a separate window with the list.\n\n2) Click a tile to show the material over the scene on the control preview and presentation; click the same tile again to hide it. You can show several materials at once — one click per tile.\n\n3) On the control preview you can drag the material, resize it from the corners, and rotate it; the × button closes that material (others stay open).\n\n4) In the materials window, the + / − magnifiers are zoom tools: pick one, then click the material on the control preview.\n\nChanging scenes clears material overlays. Scene description and field effects are separate from materials.',
    'help.section.players.title': 'Players',
    'help.section.players.body':
      'The Players item in the header stores a local library of live players on this computer (not inside the project file).\n\n1) Open Players in the header.\n\n2) Add player — name and image are required. A progress dialog appears while saving.\n\n3) On the right — player token preview: a circle with a colored ring, avatar inside (drag to recenter; mouse wheel to zoom), name on a dark plate, and a ring color picker.\n\n4) Teams are flat groups with no nesting: create a team and drag a player into it or into No team.\n\nCampaign NPCs are placed separately: in Scene editor open the NPCs accordion and drag project characters onto the map — they appear as the same circular token.\n\nDuring a session you can change the size of circular player and NPC tokens with the Play token size slider on the control panel (see Control panel).',

    'help.section.npcs.title': 'NPCs',
    'help.section.npcs.body':
      'NPCs are campaign characters with an avatar, description, and one-way relations between them. They belong to the project.\n\nIn the editor:\n\n1) Under Game properties, click NPCs to open the character editor window.\n\n2) Add — enter a unique name and a required avatar (PNG, JPG, or WebP) via Choose image or by dropping a file. Fill in the description if needed.\n\n3) On the left — character list: search, reorder by drag-and-drop; the ⋮ menu only deletes (with confirmation; relations to that character are removed too). Characters can be organized into groups (and subgroups): create a group and drag NPCs into it, or leave them Ungrouped. The relation graph has a group filter.\n\n4) In the center — relation graph: drag an arrow from one character to another and enter a required relation title.\n\n5) On the right — selected character card: circular token, ring color, avatar position (drag and mouse wheel to zoom), name, description, group, and relations. Type sets disposition: hostile, neutral, or friendly — it controls the ring color on the map.\n\n6) In Scene editor, expand NPCs and drag a character onto the map. The circular marker can be moved and resized.\n\nDuring a session on the control panel you can move NPC tokens; right-click a marker:\n• if inactive — Make active;\n• if active — Open information (NPC card), change type (hostile / neutral / friendly), and Make inactive.\nOn presentation tokens are display-only. See Control panel for more.',

    'help.section.session.title': 'Starting a session',
    'help.section.session.body':
      'When your campaign is ready, you can start playing.\n\nStandard start:\n\n1) On the story map, right-click the starting card → Start scene.\n\n2) Click Run in the editor header (left part of the button).\n\nRun with players: use the chevron on the right of Run → Run with players — pick players and/or teams. The selection lasts for the session; the control panel shows Show players / Hide players. Changing scenes hides player tokens.\n\nQuick start from any card: right-click the card on the map → Start from this scene. Presentation and the control panel open at that spot right away.\n\nPresentation (for players) and the Control panel (for you) open. The editor dims while the show runs — that is expected.\n\nReturn to prep:\n\n1) On the control panel, click Turn off or End presentation (when there is nowhere left to go).\n\n2) Wait until both windows close.\n\nMove the Presentation window to a second monitor, projector, or TV and go fullscreen (F11). Players see only the image, video, and effects — not your buttons.',

    'help.section.controlPanel.title': 'Control panel',
    'help.section.controlPanel.body':
      'The control panel is your desk during play. Players watch presentation; you run everything from here.\n\nTop-left: Tools, then effects and storyline. On the right: a mini copy of the player screen, branch options, and music.\n\nUnder Tools, the book button opens a separate window with the current scene’s formatted description. If there is no description, the button is disabled — the tooltip reads “No description”. The materials button (map icon) opens a window with the campaign materials list — see the Materials section for details. The NPCs button (colored person icon) opens the characters window — see the NPCs section.\n\nScreen preview shows what players see. Paint effects here — they appear on the big screen right away for both image and video scenes. If the scene has traps, they appear on the preview: right-click a marker to reveal, activate, or disarm (see Traps). Non-player, NPC, and player tokens are also visible on the preview: you can move them and resize them from the yellow corner until the session ends; right-click an NPC token to activate / open information / change type / make inactive (see NPCs). On video scenes, transport controls and the scrub bar stay at the bottom of the preview.\n\nAbove the preview:\n• Snap tokens to grid — when the scene grid is on, dragged tokens (non-player, NPC, players) snap to cells;\n• Play token size — shared scale for circular player and NPC tokens on the preview and presentation;\n• Show players / Hide players — after Run with players.\n\nStoryline lists episodes you have visited. The current scene is marked CURRENT SCENE. Click an earlier step to move the party back to that spot and add a new history entry.\n\nStop presentation ends the show and unlocks the editor.',

    'help.section.transitions.title': 'Scene transitions',
    'help.section.transitions.body':
      'Where you can go next appears under Branch options on the control panel. These are outgoing arrows from the current card on the map.\n\n1) Read the OPTION 1, OPTION 2 cards — they show target scene names.\n\n2) Click Switch on the choice you want.\n\n3) Presentation updates image and music; storyline adds a new step.\n\nIf there are no options (end of a branch), you will see No transitions available. Click End presentation to close the show windows.\n\nOptions only exist where you drew arrows on the map in the editor.',

    'help.section.music.title': 'Music on the control panel',
    'help.section.music.body':
      'The Music section mirrors what you set in the editor — Scene music and Game music separately.\n\n▶ play, ⏸ pause, ⏹ stop. Auto/Manual and Loop/Once show how each track was configured in the editor.\n\nClick the progress bar to seek (when duration is known). ← → on the bar (when focused) skip 5 seconds back or forward.\n\nIf music does not start on its own, press ▶ once — after your click, sound is usually allowed. If a file still will not play, check the format (MP3, WAV, etc.).',

    'help.section.effects.title': 'Field and action effects',
    'help.section.effects.body':
      'Effects work on image and video scenes. Paint in Screen preview — players see the same on presentation.\n\nPick a tool on the left:\n• Field effects (fog, rain, fire, water) — hold the left button and brush on the map.\n• Action effects (lightning, sunbeam, freeze, darkness, poison cloud, explosion) — click or short stroke; some include sound.\n\nIf Darken scene is enabled in scene properties, a Darkness control section appears with the Opening brush 🔦 and Closing brush ⬛. Opening brush clears darkness on both screens at once; Closing brush covers revealed areas again. Unrevealed areas stay fully black for players and half-dark on your preview. The state is remembered while the show runs and you return to the same graph card. This is not the same as the Darkness 🌑 action effect.\n\nEraser 🧹 — for field effects (fog, rain, fire, water), brush like the Opening brush for darkness: only the stroke area is erased. Action effects (lightning, sunbeam, etc.) are removed whole when you click or drag over them. Clear effects removes everything at once.\n\nBrush radius under the panel — higher values mean a wider stroke.',

    'help.section.presentation.title': 'Presentation screen',
    'help.section.presentation.body':
      'Presentation is what players see: the scene image or video (with rotation from the editor) according to your settings.\n\nEffects from the control panel draw on top. There are no GM menus or buttons here — players cannot click the map, traps, or tokens.\n\nIf Darken scene is enabled, players first see a fully black screen. The GM reveals the map with the Opening brush and can cover areas again with the Closing brush on the control panel.\n\nWhen you switch scenes from the control panel, the image updates automatically. Move the window to the display players watch and hide the taskbar if needed.\n\nA blank or dark screen usually means the scene has no preview — add one in scene properties in the editor.',

    'help.section.importExport.title': 'Import and export',
    'help.section.importExport.body':
      'A project is stored as a .ttrpg.zip file — images, sounds, and settings are already inside.\n\nBackup or move to another PC:\n\n1) Project → Export.\n\n2) Choose the project and where to save.\n\n3) Copy the .ttrpg.zip to a USB drive, cloud, or another computer.\n\nLoad a backup:\n\n1) Project → Import.\n\n2) Select the .ttrpg.zip.\n\n3) The project appears on the home screen.\n\nLarge archives show a progress bar. You do not need to move images and music separately — everything is in the archive.',

    'help.section.settings.title': 'Settings, language and updates',
    'help.section.settings.body':
      'Settings → Enter license key — add or change your license. About license — check status and expiry.\n\nCheck for updates (installed app with active license) — if a new version exists, you can download and restart.\n\nLanguage → Русский or English changes the interface. Your choice is saved between launches.\n\nThe version number is in the header, right of the menus. About → About opens product info and support contacts.',

    'updates.dialogTitle': 'Updates',
    'updates.checking': 'Checking for updates…',
    'updates.available': 'A new version is available: {version}.',
    'updates.current': 'You have the latest version ({version}).',
    'updates.error': 'Could not check for updates: {message}',
    'updates.notPackaged': 'Updates can only be checked in the installed application.',
    'updates.noLicense': 'An active license is required.',
    'updates.download': 'Update',
    'updates.downloading': 'Downloading…',
    'updates.stageLine': 'Stage: {stage}',
    'updates.stage.checking': 'checking for updates',
    'updates.stage.available': 'version {version} available',
    'updates.stage.not-available': 'up to date',
    'updates.stage.downloading': 'downloading{percent}',
    'updates.stage.installing': 'installing and restarting',
    'updates.stage.error': 'error',
    'updates.stagePercent': ' ({percent}%)',

    'projectMenu.home': 'Home',
    'projectMenu.import': 'Import',
    'projectMenu.importFoundry': 'Import from Foundry',
    'projectMenu.export': 'Export',
    'projectMenu.noProjects': 'No saved projects',

    'foundryImport.title': 'Import from Foundry',
    'foundryImport.hint':
      'Choose a Foundry VTT world or module folder or archive (version 11+). A new project will be created.',
    'foundryImport.sourceType': 'SOURCE TYPE',
    'foundryImport.folder': 'Folder',
    'foundryImport.archive': 'Archive (.zip / .fvtt)',
    'foundryImport.source': 'SOURCE',
    'foundryImport.chooseFolder': 'Choose folder',
    'foundryImport.chooseArchive': 'Choose archive',
    'foundryImport.noSourceSelected': 'Nothing selected',
    'foundryImport.import': 'Import',

    'fileMenu.rename': 'Rename project',

    'scenes.search': 'Search scenes…',
    'scenes.new': '+ New scene',
    'scenes.dropHint': 'Drop images or videos',
    'scenes.batchTitle': 'Creating scenes',
    'scenes.batchProgress': 'Scene {current} of {total}',
    'scenes.dropSkippedTitle': 'Some files were not added',
    'scenes.dropSkippedIntro': 'These files were skipped:',
    'scenes.dropSkippedUnsupported': 'unsupported format',
    'scenes.dropSkippedNoPath': 'could not resolve file path',
    'scenes.inspectorGame': 'Game properties',
    'scenes.inspectorScene': 'Scene properties',
    'scenes.projectLabel': 'Project: {name}',
    'scenes.selectHint': 'Select a scene on the left to edit its properties.',
    'scenes.openProjectHint': 'Open a project to edit the campaign and scenes.',

    'rename.title': 'Rename project',
    'rename.projectName': 'PROJECT NAME',
    'rename.projectPlaceholder': 'Project name…',
    'rename.projectMin': 'At least 3 characters.',
    'rename.projectDup': 'A project with this name already exists.',
    'rename.fileName': 'PROJECT FILE NAME',
    'rename.fileInvalid': 'At least 3 characters; forbidden characters <>:"/\\|?*',
    'rename.fileDup': 'A project file with this name already exists.',
    'rename.saving': 'Saving…',

    'export.title': 'Export project',
    'export.project': 'PROJECT',
    'export.hint':
      'Select storylines to export. The archive will include the chosen lines, their scenes, and assets. If the project has NPCs, the next step lets you choose which ones to include.',
    'export.npcsTitle': 'Export NPCs',
    'export.npcsHint':
      'Select NPCs to export. Relations between selected NPCs and their groups are included.',
    'export.selectAllNpcs': 'Select all',
    'export.next': 'Next',
    'export.back': 'Back',
    'export.exporting': 'Exporting…',
    'export.saveAs': 'Save as…',

    'storyline.section': 'STORYLINE',
    'storyline.main': 'Main storyline',
    'storyline.loading': 'Loading storylines…',
    'storyline.empty': 'This project has no storylines marked with START or SIDE badges.',
    'storyline.mainExistsHint': 'main storyline already exists in this project',

    'importSource.title': 'Import',
    'importSource.type': 'IMPORT TYPE',
    'importSource.fromProject': 'From project',
    'importSource.fromFile': 'From file',
    'importSource.project': 'PROJECT',
    'importSource.file': 'FILE',
    'importSource.chooseFile': 'Choose file',
    'importSource.noFileSelected': 'No file selected',
    'importSource.noOtherProjects': 'No other projects available to import from.',
    'importSource.fileOnlyHint': 'Choose a project file (.ttrpg.zip) for a full import.',

    'importStoryline.title': 'Import storylines',
    'importStoryline.source': 'SOURCE',
    'importStoryline.continue': 'Continue',
    'importStoryline.import': 'Import',
    'importStoryline.conflictsTitle': 'Duplicate scene titles',
    'importStoryline.conflictsHint':
      'Imported storylines contain scenes with the same titles as in the current project. Choose what to do for each.',
    'importStoryline.createNewScene': 'Create new scene',
    'importStoryline.useExistingScene': 'Use existing «{title}»',
    'importStoryline.reportTitle': 'Import complete',
    'importStoryline.reportLines': 'Storylines imported: {count}',
    'importStoryline.reportScenesCreated': 'New scenes created: {count}',
    'importStoryline.reportScenesReused': 'Existing scenes reused: {count}',
    'importStoryline.reportNpcsCreated': 'New NPCs created: {count}',
    'importStoryline.reportNpcsReused': 'Existing NPCs reused: {count}',
    'importStoryline.reportNodes': 'Graph cards added: {count}',
    'importStoryline.reportEdges': 'Connections added: {count}',
    'importStoryline.reportAssetsCopied': 'Asset files copied: {count}',
    'importStoryline.reportAssetsReused': 'Assets reused: {count}',
    'importStoryline.reportRenamedSides': 'Renamed side storylines: {names}',
    'importStoryline.npcConflictsTitle': 'Duplicate NPC names',
    'importStoryline.npcConflictsHint':
      'Imported storylines contain NPCs with the same names as in the current project. Choose what to do for each.',
    'importStoryline.createNewNpc': 'Create new NPC',
    'importStoryline.useExistingNpc': 'Use existing «{name}»',

    'confirmDelete.title': 'Delete project',
    'confirmDelete.body':
      'Permanently delete project “{name}”? The file and cache will be removed from disk.',
    'confirmDelete.failedTitle': 'Could not delete',

    'confirmDeleteScene.title': 'Delete scene',
    'confirmDeleteScene.body': 'Delete scene “{name}”? This cannot be undone.',

    'picker.title': 'Projects',
    'picker.newPlaceholder': 'New project name…',
    'picker.create': 'Create project',
    'picker.search': 'Search campaigns…',
    'picker.searchEmpty': 'No matches.',
    'picker.existing': 'EXISTING',
    'picker.lockedHint':
      'Opening and creating projects require an active license. The list still shows files in the app folder.',
    'picker.empty': 'No projects yet.',
    'picker.projectMenu': 'Project menu',
    'picker.openDisabled': 'Open project — after license activation',
    'picker.opening': 'Opening…',
    'picker.defaultName': 'My campaign',

    'campaign.label': 'GAME AUDIO',
    'campaign.noFiles': 'No files yet. Add audio.',
    'campaign.auto': 'Auto',
    'campaign.loop': 'Loop',
    'campaign.removeTitle': 'Remove from campaign',
    'campaign.upload': 'Upload',
    'drop.hintAudio': 'Drop audio files here',
    'drop.hintPreview': 'Drop an image or video',

    'materials.open': 'Materials',
    'materials.managerTitle': 'Materials',
    'materials.add': 'Add',
    'materials.addTitle': 'New material',
    'materials.editTitle': 'Edit material',
    'materials.savingTitle': 'Saving material',
    'materials.savingWait': 'Please wait…',
    'materials.savingProgress': 'Material save progress',
    'materials.edit': 'Edit',
    'materials.search': 'Search materials…',
    'materials.searchEmpty': 'No matches.',
    'materials.empty': 'No materials yet.',
    'materials.addPrompt': 'Add a material',
    'materials.name': 'NAME',
    'materials.namePlaceholder': 'Material name…',
    'materials.nameRequired': 'Name is required.',
    'materials.nameDup': 'A material with this name already exists.',
    'materials.image': 'IMAGE',
    'materials.imageEmpty': 'No image selected',
    'materials.imageRequired': 'Choose an image.',
    'materials.chooseImage': 'Choose image',
    'materials.dropHint': 'Drop an image (PNG, JPG, WebP)',
    'materials.tileMenu': 'Material menu',
    'materials.windowEmpty': 'Add materials in the editor.',
    'materials.closeOverlay': 'Close materials',
    'materials.rotateOverlay': 'Rotate',
    'materials.deleteTitle': 'Delete material',
    'materials.deleteConfirm': 'Are you sure you want to delete material “{name}”?',
    'materials.zoomIn': 'Zoom in',
    'materials.zoomOut': 'Zoom out',
    'materials.zoomInHint': 'Click the material on the control preview to zoom in.',
    'materials.zoomOutHint': 'Click the material on the control preview to zoom out.',
    'materials.zoomIdleHint': 'Pick a magnifier, then click the material on the control preview.',

    'players.managerTitle': 'Players',
    'players.add': 'Add player',
    'players.search': 'Search players…',
    'players.selectPrompt': 'Select a player or add a new one.',
    'players.name': 'NAME',
    'players.namePlaceholder': 'Player name…',
    'players.ringColor': 'RING COLOR',
    'players.chooseImage': 'Choose image',
    'players.dropHint': 'Drop an image (PNG, JPG, WebP)',
    'players.savingTitle': 'Saving player',
    'players.savingWait': 'Please wait…',
    'players.ungrouped': 'No team',
    'players.addTeam': 'New team',
    'players.teamName': 'Team name',
    'players.teamColor': 'Team color',
    'players.teamMenu': 'Team menu',
    'players.dropHere': 'Drop a player here',
    'players.deleteTitle': 'Delete player',
    'players.deleteConfirm': 'Are you sure you want to delete player “{name}”?',
    'players.deleteTeamTitle': 'Delete team',
    'players.deleteTeamConfirm':
      'Are you sure you want to delete team “{name}”? Players will become ungrouped.',

    'npcs.open': 'NPCs',
    'npcs.editorTitle': 'NPCs',
    'npcs.add': 'Add',
    'npcs.addTitle': 'New NPC',
    'npcs.editTitle': 'Edit NPC',
    'npcs.savingTitle': 'Saving NPC',
    'npcs.savingWait': 'Please wait…',
    'npcs.savingProgress': 'NPC save progress',
    'npcs.graphLoading': 'Loading graph…',
    'npcs.edit': 'Edit',
    'npcs.tileMenu': 'NPC menu',
    'npcs.search': 'Search NPCs…',
    'npcs.searchEmpty': 'No matches.',
    'npcs.empty': 'No NPCs yet.',
    'npcs.selectPrompt': 'Select an NPC in the list or on the graph.',
    'npcs.name': 'NAME',
    'npcs.namePlaceholder': 'Character name…',
    'npcs.nameRequired': 'Name is required.',
    'npcs.nameDup': 'An NPC with this name already exists.',
    'npcs.avatar': 'AVATAR',
    'npcs.avatarEmpty': 'No avatar selected',
    'npcs.avatarRequired': 'Choose an avatar.',
    'npcs.chooseAvatar': 'Choose avatar',
    'npcs.dropHint': 'Drop an image (PNG, JPG, WebP)',
    'npcs.disposition': 'TYPE',
    'npcs.disposition.hostile': 'Hostile',
    'npcs.disposition.neutral': 'Neutral',
    'npcs.disposition.friendly': 'Friendly',
    'npcs.makeHostile': 'Make Hostile',
    'npcs.makeNeutral': 'Make Neutral',
    'npcs.makeFriendly': 'Make Friendly',
    'npcs.inactive': 'Inactive',
    'npcs.makeActive': 'Make Active',
    'npcs.openInfo': 'Open information',
    'npcs.ringColor': 'TOKEN RING COLOR',
    'npcs.description': 'DESCRIPTION',
    'npcs.descriptionPlaceholder': 'Character description…',
    'npcs.descriptionEmpty': 'No description',
    'npcs.relations': 'Relations',
    'npcs.untitled': 'Untitled',
    'npcs.deleteTitle': 'Delete NPC',
    'npcs.deleteConfirm':
      'Are you sure you want to delete NPC “{name}”? All of their relations will be removed.',
    'npcs.relationCreateTitle': 'Relation name',
    'npcs.relationEditTitle': 'Relation name',
    'npcs.relationLabel': 'NAME',
    'npcs.relationLabelPlaceholder': 'e.g. friends, rivals…',
    'npcs.relationLabelRequired': 'Relation name is required.',
    'npcs.relationEdit': 'Edit',
    'npcs.relationDelete': 'Delete',
    'npcs.relationDeleteTitle': 'Delete relation',
    'npcs.relationDeleteConfirm': 'Delete relation “{name}”?',
    'npcs.graphZoomBar': 'Graph zoom',
    'npcs.graphZoomIn': 'Zoom in',
    'npcs.graphZoomOut': 'Zoom out',
    'npcs.graphFitAll': 'Fit view',
    'npcs.windowEmpty': 'Add NPCs in the editor.',
    'npcs.selectToShow': 'Select a character in the list — they will appear on screen.',
    'npcs.closeOverlay': 'Close all',
    'npcs.rotateOverlay': 'Rotate',
    'npcs.zoomIn': 'Zoom in',
    'npcs.zoomOut': 'Zoom out',
    'npcs.zoomInHint': 'Click the avatar on the control preview to zoom in.',
    'npcs.zoomOutHint': 'Click the avatar on the control preview to zoom out.',
    'npcs.zoomIdleHint': 'Pick a magnifier, then click the avatar on the control preview.',
    'npcs.addToMap': 'Add to map',
    'npcs.addToMapHint': 'Place a token on the current scene until the session ends. You can add it more than once.',
    'npcs.addToMapNoScene': 'No active scene — start the game first.',
    'npcs.dragToMapHint':
      'Drag a tile onto the control preview or press Add to map. Session-only token.',
    'npcs.removeFromMap': 'Remove from map',
    'npcs.ungrouped': 'Ungrouped',
    'npcs.addGroup': 'New group',
    'npcs.editGroup': 'Edit group',
    'npcs.deleteGroup': 'Delete group',
    'npcs.groupName': 'Group name',
    'npcs.groupColor': 'Color',
    'npcs.groupNameRequired': 'Group name is required.',
    'npcs.groupNameDup': 'A group with this name already exists.',
    'npcs.deleteGroupTitle': 'Delete group',
    'npcs.deleteGroupConfirm':
      'Delete group “{name}”? NPCs will become ungrouped; child groups will move up one level.',
    'npcs.addSubgroup': 'Add subgroup',
    'npcs.group': 'GROUP',
    'npcs.graphFilterAll': 'All',
    'npcs.graphFilterUngrouped': 'Ungrouped',
    'npcs.graphFilter': 'Graph filter',

    'scene.title': 'SCENE TITLE',
    'scene.description': 'DESCRIPTION',
    'scene.descriptionEmpty': 'no description',
    'scene.descriptionModalTitle': 'Scene description',
    'scene.descriptionPlaceholder': 'Enter scene description…',
    'scene.descriptionToolbar': 'Formatting',
    'scene.descriptionBold': 'Bold',
    'scene.descriptionItalic': 'Italic',
    'scene.descriptionUnderline': 'Underline',
    'scene.descriptionHeading2': 'Heading',
    'scene.descriptionHeading3': 'Subheading',
    'scene.descriptionQuote': 'Quote',
    'scene.descriptionBulletList': 'Bullet list',
    'scene.descriptionOrderedList': 'Numbered list',
    'scene.descriptionLink': 'Link',
    'scene.descriptionLinkPrompt': 'Link URL',
    'scene.preview': 'SCENE PREVIEW',
    'scene.previewHint': 'Image file (PNG, JPG, WebP, GIF, etc.).',
    'scene.previewEmpty': 'No preview',
    'scene.previewBusy': 'Loading and optimizing image…',
    'scene.previewBusySelecting': 'Choose a file…',
    'scene.previewOptimizing': 'Preview is ready. Optimizing in the background…',
    'scene.previewReady': 'Preview is ready',
    'scene.previewFailed': 'Preview was added, but optimization failed',
    'scene.change': 'Change',
    'scene.clear': 'Clear',
    'scene.autostart': 'Autostart',
    'scene.darkenScene': 'Darken scene',
    'scene.rotate': 'Rotate',
    'scene.audio': 'SCENE AUDIO',
    'scene.sideStoryLineTitle': 'Side storyline title',
    'scene.removeTitle': 'Remove from scene',
    'scene.branching': 'BRANCHING',
    'scene.branchingHint':
      'Drag a scene from the list onto the graph. One card can branch to several targets — one link per target scene. You cannot link twice to the same target (including a second card of the same scene).',

    'sceneCard.current': 'CURRENT',
    'sceneCard.menu': 'Scene menu',

    'graph.badgeStart': 'START',
    'graph.badgeSideStory': 'SIDE',
    'graph.untitled': 'Untitled',
    'graph.videoBadge': 'Video',
    'graph.audioBadge': 'Audio',
    'graph.loop': 'Loop',
    'graph.autoplay': 'Autoplay',
    'graph.previewAutostart': 'Preview autostart',
    'graph.videoLoop': 'Video loop',
    'graph.zoomBar': 'Graph zoom',
    'graph.zoomIn': 'Zoom in',
    'graph.zoomOut': 'Zoom out',
    'graph.fitAll': 'Fit view',
    'graph.startScene': 'Start scene',
    'graph.unsetStartScene': 'Clear start scene mark',
    'graph.sideStoryStartScene': 'Side storyline start scene',
    'graph.unsetSideStoryStartScene': 'Clear side storyline start mark',
    'graph.runFromScene': 'Start from this scene',

    'control.remoteTitle': 'CONTROL PANEL',
    'control.instruments': 'TOOLS',
    'control.descriptionTool': 'Description',
    'control.materialsTool': 'Materials',
    'control.npcsTool': 'NPCs',
    'control.descriptionMissing': 'No description',
    'control.effects': 'EFFECTS',
    'control.tools': 'Cleanup',
    'control.fieldEffects': 'Field effects',
    'control.actionEffects': 'Action effects',
    'control.eraser': 'Eraser',
    'control.clearEffects': 'Clear effects',
    'control.fog': 'Fog',
    'control.rain': 'Rain',
    'control.fire': 'Fire',
    'control.water': 'Water',
    'control.darkness': 'Darkness',
    'control.darknessControl': 'Darkness control',
    'control.explorerBrush': 'Opening brush',
    'control.closerBrush': 'Closing brush',
    'control.lightning': 'Lightning',
    'control.sunbeam': 'Sunbeam',
    'control.freeze': 'Freeze',
    'control.poisonCloud': 'Poison cloud',
    'control.explosion': 'Explosion',
    'control.brushRadius': 'Brush radius',
    'control.effectsSound': 'Effects sound',
    'control.storyLine': 'STORYLINE',
    'control.gotoScene': 'Go to this scene',
    'control.currentSceneBadge': 'CURRENT SCENE',
    'control.passed': 'Visited',
    'control.noActiveScene': 'No active scene.',
    'control.screenPreview': 'Screen preview',
    'control.npcTokenScale': 'Play token size',
    'control.resizeToken': 'Resize',
    'control.snapTokensToGrid': 'Snap tokens to grid',
    'control.snapTokensToGridNoGrid': 'Grid is off on this scene — snap has no effect',
    'control.stopPresentation': 'Turn off',
    'control.showPlayers': 'Show players',
    'control.hidePlayers': 'Hide players',
    'control.videoBrushHint':
      'Video preview: effect brush is disabled (like on the presentation screen — overlay is for images only).',
    'control.branches': 'Branch options',
    'control.returnToMainStory': 'Return to main story',
    'control.sideStoryLines': 'Side storylines',
    'control.option': 'OPTION {n}',
    'control.unnamed': 'Untitled',
    'control.switchScene': 'Switch',
    'control.noBranches': 'No transitions available.',
    'control.endPresentation': 'End presentation',
    'control.music': 'Music',
    'control.sceneMusic': 'SCENE MUSIC',
    'control.gameMusic': 'GAME MUSIC',
    'control.noSceneAudio': 'No audio in the current scene.',
    'control.noGameAudio': 'No game audio.',
    'control.modeAuto': 'Auto',
    'control.modeManual': 'Manual',
    'control.once': 'Once',
    'control.loop': 'Loop',
    'control.scrubSeek': 'Click to seek',
    'control.durationUnknown': 'Duration unknown',
    'control.pauseSceneMusic': 'Paused (scene)',
    'control.pauseSceneMusicTitle': 'Scene has music',
    'control.pauseCampaignTitle': 'Paused: scene has music',
    'control.playFailed': 'Could not start playback.',
    'control.audioAutoplayBlocked': 'Autoplay was blocked (user gesture required) or playback failed.',
    'control.audioNoUrl': 'No URL',
    'control.audioNoUrlDetail': 'Could not get dnd://asset URL for audio.',
    'control.audioBlocked': 'Error / blocked',
    'control.audioError': 'Error',
    'control.audioMediaError': 'MediaError code={code} (1=ABORTED, 2=NETWORK, 3=DECODE, 4=SRC_NOT_SUPPORTED)',
    'control.audioLoading': 'Loading…',
    'control.audioPlaying': 'Playing',
    'control.audioPaused': 'Paused',
    'control.audioStopped': 'Stopped',
    'control.previewTrackLabel': 'Preview (no captions)',
    'control.transportPlay': 'Play',
    'control.transportPause': 'Pause',
    'control.transportStop': 'Stop',
    'control.volume': 'Volume',
  },
};

export function translateEditorMessage(
  locale: EditorLocale,
  key: string,
  vars?: Record<string, string | number>,
): string {
  let s = EDITOR_MESSAGES[locale][key] ?? EDITOR_MESSAGES.ru[key] ?? key;
  if (vars) {
    for (const [k, v] of Object.entries(vars)) {
      s = s.split(`{${k}}`).join(String(v));
    }
  }
  return s;
}
