tbook четыре месяца спустя: читалки, словари и v2 формата
Вы читаете роман на английском, нажимаете на незнакомое слово — и читалка подсвечивает в переводе совсем другое. Файл при этом валиден: проверка сказала «всё хорошо». Именно такие случаи заставили меня переделать .tbook.
В первой статье я рассказывал, как сделал формат книги с готовым переводом и пословным выравниванием: нажимаешь на слово в оригинале, видишь перевод предложения с нужной подсветкой. Всё работает офлайн, потому что книга уже содержит в себе переводы. За четыре месяца вокруг этой идеи выросли Android-приложение в Google Play, читалка для компьютера, офлайн-словари, веб-конвертер, общая библиотека и локальный GUI. А 29 сентября вышла вторая версия формата. Вот что изменилось для читателя — и почему одного улучшения моделей оказалось мало.
Из эксперимента — в повседневную читалку
Android. TReader дошёл до версии 1.14. В нём появились режим для e-ink без анимаций, транскрипция английских слов с выбором американского и британского произношения, озвучка слова или предложения через TTS. Билингвальный режим показывает оригинал и перевод вперемешку, в двух колонках или строка за строкой. Настройки, помощь и тур доступны на девяти языках;
Десктоп. TReader Desktop собирается для Windows, macOS и Linux. Внутри Tauri v2: Rust занимается книгой, поиском и словарями, Svelte 5 рисует интерфейс в системном WebView. Бинарь — около 6 МБ. Здесь тоже есть перевод по нажатию, транскрипция, билингвальный режим, озвучка, сноски, изображения, таблицы и поиск. На Linux доступны .deb, .rpm и .AppImage; macOS-сборка пока не нотаризована, что осложняет первый запуск.
С удовольствием скооперируюсь с macOS/iOS разработчиками, чтобы выпустить версии под iPhone и Mac.
Обе читалки открывают книги v1 и v2, так что старую библиотеку переносить не нужно.
Словари. Тап отвечает на вопрос «что слово значит здесь», но учащемуся нужна и словарная статья: часть речи, род, произношение, другие значения и формы. Для этого появился .tdict — SQLite-файл, который читалки скачивают отдельно и затем используют офлайн. В нём три таблицы: метаданные, словарные записи и словоформы. При поиске лемма стоит выше совпадения по форме: так точное слово не теряется среди похожих результатов. Данные приходят из Wiktionary через kaikki.org и распространяются по CC BY-SA 4.0. Уже собраны все 20 направленных пар между английским, русским, немецким, испанским и итальянским. Нужную пару можно скачать с внутреннего сервера переводов из приложения; всю коллекцию тянуть не приходится.
Конвертация стала доступнее
Открытый локальный конвертер никуда не делся. Но для сборки из исходников и локального LaBSE-алайнера нужны Go, Python и модель объёмом около 1,8 ГБ. Поэтому появился веб-конвертер: загружаете EPUB или FB2, выбираете язык и модель, следите за задачей и получаете .tbook. За ним работает тот же конвертер из открытого репозитория. Результат может пополнить общую библиотеку с поиском по книгам и языкам; Telegram-бот позволяет искать в каталоге.
Скрытый текст
Я очень долго размышлял на тему этичности этой части. И всё еще не могу на неё ответить. Создателя Флибусты мы скорее любим (Stiver, мы помним о тебе!)…
Мне пошарили телеграм-бота @TbooksBot, где ребята шарят переведенные книги. Я не имею к нему отношения, хотя и шарю свои книги. Но, он появился (видимо, неизбежно), о чем я не могу не упомянуть.
Для тех, кто предпочитает запускать всё у себя, в октябре вышел GUI-конвертер на Wails: мастер настройки ключей, оценка книги до конвертации, очередь и живой прогресс. Есть готовые бинарники для основных ОС. За пределами Linux пока мало тестировался.
Конвертер научился помнить книгу
Раньше одна Irene за несколько глав успевала стать «Айрин», «Ирин» и «Ирен»: переводчик обрабатывал небольшие порции текста и забывал прежний выбор. Теперь конвертер заранее собирает глоссарий имён и терминов и передаёт его с каждым запросом. Его можно построить до перевода и исправить вручную через --only-glossary — это добавил Никита Пимкин (NikPim), первый внешний контрибьютор. Спасибо!
Измерения помогли подобрать размер: 40 записей покрывали 61,9 % упоминаний терминов в тестовой книге, 300 — 97,2 %. Для персонажей появились теги рода. На тексте, которого модель не знала заранее, правильное согласование выросло с 45,2 до 97,6 %.
Глоссарий собирается отдельно для каждого языка перевода. Это важно: если отправить русские варианты имён в запрос для немецкого, модель может послушно протащить их в немецкий текст. А предварительный просмотр через --only-glossary даёт шанс исправить имя героини до того, как оно разойдётся по сотням предложений.
Выравнивание тоже изменилось. Локальный LaBSE на CPU разбирает основную массу предложений, LLM дорабатывает примерно 4 % сложных случаев. Гибридный режим работает в 5–8 раз быстрее чистого LLM-выравнивания при лучшем покрытии слов. Отдельная обработка идиом научила алайнер связывать gave up с «бросил» целиком, а не только через up: на проверенном романе доля работающих тапов выросла с 79 до 96 %.
Цифры покрытия требуют осторожности: слово может подсвечиваться и всё равно вести не туда. Поэтому варианты алайнера сравнивались по тому, что пользователь увидит при тапе, а не только по заполненности ссылок. Для идиом это особенно заметно: если gave up разбилось на две независимые части, формально подсветка есть, но смысл потерян.
Появились корректура перевода, нормализация пунктуации для русского, немецкого и французского, а также новые провайдеры: gonka, Claude через локальный CLI, Ollama и llama.cpp. Теперь можно выбрать между облачными и локальными моделями, хотя качество у них разное. Около двух десятков отчётов об экспериментах лежат рядом с конвертером в виде issues на гитхабе: полезные изменения проходили слепое сравнение, а идеи, которые проиграли, не прятались. Больше шагов в пайплайне не гарантируют лучший результат.
Почему пришлось выпустить .tbook v2
В сентябре я сравнил девять вариантов формата на одном корпусе. Только 62,6 % тапов в v1 давали чистую подсветку. Это результат конкретного теста, но причины оказались в конструкции формата. v1 собирал страницу из предложений, поэтому ошибка сегментатора могла разорвать слово. Ссылки на слова можно было перемешать, и файл всё равно проходил структурную проверку. Цифры вообще не распознавались как слова для тапа. Добавление языка требовало пересобрать главы.
Я проверял и другие пути: SQLite, бинарное представление, оверлей поверх EPUB, выравнивание прямо в читалке. Последний вариант воспроизводил только 47,5 % старых тапов. Выбор пал на знакомый ZIP с JSON, но с другой моделью текста и ссылок. В проверенном наборе v1 ни один из 132 числовых токенов не был доступен для тапа — мелкая деталь, пока не пытаешься прочитать адрес или дату.
В v2 я поменял четыре вещи.
Абзац хранится целиком. Предложения стали диапазонами внутри него: "text": "Hello. Goodbye.", "sents": [[0, 7], [7, 15]]. Если сегментатор ошибётся на границе, текст страницы останется исходным: читалка больше не склеивает его из нарезанных фраз. Сегментация влияет на перевод, но не калечит оригинал на экране.
У каждого языка свой оверлей. Оригинал лежит в скелете книги, переводы и ссылки — в отдельных файлах по главам и языкам. Например, text/ch1.json содержит исходную главу, а gloss/ch1.ru.json — русский перевод. Новый язык можно дописать, не переписывая скелет. В тесте восьмиязычная книга уменьшилась с 6,96 до 3,55 МБ; одноязычная — с 2,19 до 1,30 МБ. Повторяющиеся диапазоны каждого слова больше не хранятся в архиве: читалка вычисляет их при показе текста по общему правилу из спецификации.
У каждого токена перевода своя ссылка. Например, 0 ведёт к одному слову оригинала, [5, 6] — сразу к двум, null означает неизвестную связь, а [] — слово, добавленное переводом. Последние два состояния нельзя смешивать: «не знаю» и «знаю, что пары нет» — разные утверждения. Это важно и при миграции старой книги: нельзя задним числом приписать ей уверенность, которой в исходных данных не было. Для языков и выражений, требующих более тонкой разметки, в формате есть дополнительные поля, но конвертер пока не заполняет их автоматически.
Файл сообщает, что проверяли. Старый показатель q измерял покрытие, а выглядел как оценка качества. В реальных книгах 75–88 % ячеек имели q = 1; даже 16 ошибочных китайских предложений получили единицу. Теперь книга хранит статус перевода, результаты проверок и список проверок, которые запускались. Если судья смотрел лишь подозрительные фразы, остальные не помечаются как проверенные. Читалка может отличить «прошло проверку» от «никто не смотрел».
Хеши записей архива ловят повреждение файла, alignDigest — расхождение между ссылками и их контрольным отпечатком. Закладки опираются на идентификаторы, полученные из содержимого главы и абзаца, поэтому могут пережить повторную конвертацию. Но ни один хеш не доказывает, что перевод правильный по смыслу. Он лишь не даёт незаметно подменить или повредить проверяемые данные.
Старые книги продолжают открываться. На восьмиязычной книге миграция из v1 сохранила прежний результат 836 496 из 836 600 тапов — 99,9876 %. Остальные 104 случая пришлись на слова, которые v1 разрезал посреди. Это процент сохранения поведения, а не процент правильных переводов.
Несколько ошибок, которые многому научили
За лето накопилось около двух десятков отчётов с проверкой гипотез, в том числе слепым сравнением вариантов. Самые полезные находки оказались неприятными.
Сильная модель испортила правильное. На «Имени розы» я отправил ей 72 подозрительных предложения. Через 32 минуты она сломала 49 верных выравниваний. Словарная проверка флаговала латынь и окситанский, которые немецко-русский словарь не мог оценить; пайплайн принимал «не знаю» за «ошибка».
Больше покрытия оказалось хуже. Новый алайнер поднял покрытие значимых слов с 97,84 до 99,57 %, но слепое сравнение дважды отдало победу прежнему варианту: лишние ссылки вели к чужим словам. Помог более осторожный алгоритм, который заполняет только пустоты. Даже у восстановленных им слов 15,6 % связей ошибочны. Читателю важно, то ли слово подсветилось, а не сколько слов подсвечено.
Контекст тоже может мешать. Одно предыдущее предложение снизило точность корректуры с 87,1 до 80,0 %; два — дали результат лучше обоих вариантов. Поэтому --context 1 инструмент теперь отвергает. А при смене модели два валидных батча оказались переводом на китайский вместо нужного языка — 16 из 544 предложений. Теперь язык проверяется отдельно.
После перевода тоже можно потерять качество. Проходка типографики меняла текст до сборки вердиктов проверок. Вердикты искались по прежней строке, и 977 французских ячеек ушли в книгу без положенных замечаний. При подготовке v2 порядок операций поменяли: сначала сохраняется результат проверок, затем правятся кавычки и пробелы.
Даже после пометки v2 как Final нашёлся реальный файл, на котором миграция упала: маркер сноски 10 одновременно попал в исключение и в ссылку. 20 октября пришлось исправить конвертер и уточнить спецификацию. Так выглядит работа с форматом после релиза.
Что дальше
В v2 уже есть места для идиом, разрывных выражений и частей слова; теперь нужно научиться заполнять их и показывать в читалках. Алайнер пока выдаёт диапазоны символов, которые при записи переводятся в токены v2; прямое выравнивание в токенах уберёт лишний шаг. Для приложений остаются полная локализация интерфейса и улучшение установки на macOS. Но главный принцип уже появился: если система чего-то не знает, книга должна честно это показывать. Валидный файл и хороший перевод — не одно и то же.
Спецификация v2 открыта по CC BY 4.0, конвертер — по MIT. Если хотите реализовать собственную читалку или проверить .tbook на своей книге, буду рад любой обратной связи.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.