Skip to content

Feature: единая система перевода текста и комиксов, словарей и AI-инструментов #154

Description

@Leostrange

Feature: единая система перевода текста и комиксов, словарей и AI-инструментов

Заголовок issue

Implement integrated text/comic translation, dictionary lookup, Explain, OCR overlay, and adaptive reader actions

Краткое описание

Необходимо завершить и связать в единую пользовательскую систему функции перевода текстовых книг и комиксов, локальных и онлайн-переводчиков, OCR, словарей, объяснения выделенного текста и хранения результатов. Пользователь должен иметь возможность выделить слово, фразу или текстовый блок и выполнить релевантное действие: получить словарную статью, перевод, объяснение, сохранить цитату, создать/изменить/удалить цветное выделение или перевести главу.

Для комиксов необходим отдельный pipeline: распознать текстовые блоки на странице, классифицировать их, перевести и показать результат поверх исходного изображения в виде настраиваемого overlay. Оригинальное изображение нельзя разрушать, а результат OCR/перевода должен сохраняться и быть повторно доступен без обязательного повторного запроса к провайдеру.

Работа должна опираться на существующие модули репозитория, а не создавать параллельную систему. В checkout уже присутствуют core-domain translation engines, engine-llm, feature-ocr, reader controllers/sheets, Room translation cache, dictionary assets/repository и настройки Translation/AI.

Контекст по текущему репозиторию

Область Уже существующая опора Что требуется довести
Перевод текста core-domain/.../translation/, MlKitTranslatorEngine, OfflineTranslationEngine, OnlineTranslationEngine, TranslationEngineSelector, online providers Единый routing, сохранение результата, понятные ошибки и подключение к reader menu
Онлайн-перевод GoogleTranslationProvider, YandexTranslationProvider, DeepLTranslationProvider, MultiProviderTranslatorEngine, OpenRouterOnlineTranslationEngine Provider/transport policy, fallback, rate-limit handling, secure settings и тесты
Локальный перевод engine-llm/.../nllb/NllbTranslatorEngine.kt, offline translation engines Явная модель локального backend, загрузка/доступность модели и единый контракт результата
Перевод комиксов feature-ocr/data/ComicTranslationPipeline.kt, DefaultComicTranslationEngine.kt, OcrPageCache, OcrRepository, ML Kit OCR Страница → OCR blocks → фильтрация → перевод → overlay → cache/history
OCR UI feature-ocr/ui/OcrScreen.kt, OcrViewModel*, OcrImageModeSection, OcrResultSections Связать ручной и автоматический сценарии с reader и переводами комиксов
Словари core-data/dictionary/, DictionaryRepository, RoomDictionaryEngine, QuickDictionaryEngine, SingleWordDictionaryResolver, dictionary_*.dbpack Надёжный lookup, fallback, настройка языковых пар, download/availability/error states
Выделенный текст ReaderSelectionSheets.kt, ReaderSelectedTextTranslationController.kt, ReaderExplainController.kt, ReaderHighlightController.kt Адаптивное меню, общий результат действий, удаление существующего highlight из UI
Сохранение переводов TranslationCacheEntry, TranslationCacheDao, RoomTranslationCacheRepository История/каталог переводов текстовых книг и комиксов, повторное открытие и удаление
Настройки feature-settings Translation/AI sections, TranslationModels.kt, CustomAiProviderConfig.kt Не дублировать настройки; Translation хранит языки/OCR/display, AI — provider/transport/model/Explain/summary

Цели

  1. Реализовать единый контракт перевода для текста, выделения, главы и OCR-блоков комикса.
  2. Поддержать локальный перевод через доступную модель/движок и онлайн-перевод через настроенный provider/API.
  3. Довести словарный lookup для одного слова и коротких фраз с корректным выбором языковой пары и fallback.
  4. Добавить Explain для слова, фразы и короткого контекста с локальным и расширенным AI-маршрутом.
  5. Сделать reader selection menu контекстным и адаптированным к reader preset, но предсказуемым по составу действий.
  6. Реализовать OCR-перевод комиксов с сохранением исходных координат блоков и overlay-настройками.
  7. Сохранить текстовые и комиксные переводы в локальную БД, чтобы пользователь мог сортировать, открывать, повторять действия и удалять результаты.
  8. Исправить текущий UX-дефект: цветное выделение создаётся, но существующее выделение нельзя удалить из пользовательского интерфейса.
  9. Сделать ошибки, недоступные языковые пары, отсутствие сети, отсутствие модели/словаря и незаполненные API credentials понятными и восстанавливаемыми.

Не входит в первую итерацию

Не следует в первой итерации реализовывать полноценное редактирование оригинальных файлов, замену исходных изображений комикса, синхронизацию переводов между устройствами, автоматическую публикацию переводов и обучение собственных OCR/translation моделей. Эти направления можно добавить после стабилизации локального pipeline и persistence.

Предлагаемая архитектура

1. Единый доменный контракт

Ввести или расширить единый контракт, который поддерживает текстовые и графические источники:

data class TranslationRequest(
    val sourceText: String,
    val sourceLanguage: String?,
    val targetLanguage: String,
    val context: TranslationContext,
    val transport: TranslationTransportPreference,
    val providerId: String? = null,
    val glossaryId: String? = null
)

data class TranslationResult(
    val sourceText: String,
    val translatedText: String,
    val sourceLanguage: String?,
    val targetLanguage: String,
    val providerId: String?,
    val transport: TranslationTransportPreference,
    val confidence: Float?,
    val createdAt: Long,
    val cacheKey: String
)

TranslationContext должен различать WORD, PHRASE, CHAPTER, OCR_BLOCK, COMIC_PAGE и EXPLAIN. Контекст влияет на routing, лимиты размера, формат prompt, выбор словаря и отображение результата.

2. Routing и fallback

Маршрутизация должна использовать существующие TranslationEngineSelector, OnlineTranslationEngine, OfflineTranslationEngine, dictionary engines и AI configuration. Рекомендуемый порядок:

Запрос Основной маршрут Fallback
Одно слово DictionaryEngine локальный/онлайн MT
Короткая фраза Dictionary, если найдено устойчивое выражение MT
Выделенный текст MT с определением языка локальный MT при отсутствии сети
Explain Local Explain configured external Explain, если включён
Глава batch/queue MT локальная модель; частичный retry
OCR-блок OCR text → MT локальный MT; оставить оригинал при ошибке
Комикс-страница OCR pipeline → block translation cache предыдущего результата

Нельзя считать отсутствие сети успешным переводом. Каждая операция должна возвращать состояние Loading, Success, Partial, Unavailable или Error с техническим кодом и локализованным сообщением.

3. Перевод текста

Для текстовых книг реализовать три сценария:

  • Выделенное слово/фраза. Нормализовать whitespace, определить язык, сначала выполнить dictionary lookup для одного слова/короткой фразы, затем при необходимости получить MT.
  • Перевод главы. Запускать через очередь с отменой, прогрессом, паузой и повтором неудавшихся сегментов. Результаты сохранять по стабильному bookId/chapterId/segmentId/sourceHash/targetLanguage/provider.
  • Перевод книги. Не блокировать UI и не выполнять бесконтрольный запрос всего документа. Использовать сегментацию, лимиты, deduplication и возможность удалить результат.

Для одного и того же исходного текста cache key должен зависеть от source hash, source/target language, контекста, версии prompt/engine и provider. Это исключает возврат устаревшего результата после изменения настроек.

4. Перевод комиксов и overlay

Расширить существующий ComicTranslationPipeline следующими стадиями:

  1. Получить страницу или изображение.
  2. Выполнить OCR и получить блоки: id, bbox, polygon при наличии, text, confidence, blockType, readingOrder.
  3. Применить фильтры: только диалоги, включать/исключать SFX, narrative и unknown blocks.
  4. Нормализовать OCR-текст с сохранением original text.
  5. Перевести блоки батчем с контролем лимитов и partial success.
  6. Сохранить ComicPageTranslation и каждый ComicTextBlockTranslation в cache/history.
  7. Отобразить overlay поверх исходной страницы, не меняя оригинальный bitmap.
  8. При повторном открытии сначала загрузить cache, затем предложить retry для незавершённых блоков.

Overlay должен поддерживать прозрачность, размер шрифта и стиль: AUTO_THEME, LIGHT, DARK. Координаты должны корректно масштабироваться при zoom и смене размера viewport. Нельзя привязывать overlay к абсолютным пикселям экрана.

5. Словари

Довести текущий dictionary stack до единого поведения:

  • использовать DictionaryRepository и существующие dictionary_*.dbpack, не обращаться к DB напрямую из UI;
  • проверять наличие пакета и языковой пары до запроса;
  • корректно различать слово, словоформу, многословное выражение и отсутствие результата;
  • учитывать source language из настроек или reliable auto-detection;
  • показывать несколько значений, транскрипцию/часть речи при наличии, примеры и источник;
  • давать понятную кнопку перехода к MT, если dictionary lookup не дал результата;
  • не падать при отсутствующем, повреждённом или ещё не распакованном словаре;
  • исправить кнопку «Словари» в настройках: переход не должен закрывать приложение, а ошибка должна отображаться inline.

5.1. UX подменю «Перевод → Словари»

Подменю должно быть самостоятельным экраном управления словарями, а не набором отдельных кнопок. Экран должен сразу показывать пользователю состояние локального словарного слоя и позволять скачать все поддерживаемые языковые пакеты одним действием.

Карточка-саммари

В верхней части экрана разместить карточку с заголовком «Словари» и кратким состоянием:

Поле Требование
Заголовок Словари
Доступно 10 языковых пакетов
Загружено динамическое значение N из 10
Дополнительное состояние например: Загружено 3 из 10, Загрузка…, Требуется место, Ошибка загрузки

Количество загруженных пакетов должно вычисляться по фактическому локальному состоянию DictionaryAssetCatalog/DictionaryAssetExtractor, а не храниться отдельно как ненадёжный счётчик. После завершения, удаления или ошибки карточка должна обновляться без перезапуска экрана.

Карточка массовой загрузки

Под summary разместить карточку загрузки со следующими элементами:

  • заголовок или пояснение: «Загрузить все словари»;
  • основной action: «Скачать все словари»;
  • отображаемый ориентировочный объём: около 750 МБ;
  • progress bar во время загрузки;
  • текстовое состояние: текущий пакет, общий прогресс, загруженный объём и оставшийся объём при доступности этих данных;
  • действия Пауза, Продолжить и Отменить для долгой загрузки;
  • после завершения — состояние Все словари загружены и возможность повторной проверки целостности.

Ориентировочный размер около 750 МБ должен быть конфигурируемым metadata-полем, а не жёстко зашитой строкой: фактический размер может измениться после обновления assets или версии словарной базы. Перед началом массовой загрузки нужно показать предупреждение о размере и проверить доступное место. Уже загруженные или корректные пакеты нельзя скачивать повторно.

При ошибке одного пакета загрузка остальных не должна silently завершаться как успешная. Пользователь должен увидеть N из 10 загружено, список неудачных пакетов и action Повторить. Повторная попытка должна продолжать загрузку с незавершённого пакета.

Список языков

Ниже разместить список всех 10 поддерживаемых словарных пакетов в фиксированном порядке или в порядке, согласованном с локалью приложения:

Код Язык Статус
en English загружено / загружается / доступно
fr Français загружено / загружается / доступно
it Italiano загружено / загружается / доступно
ja 日本語 загружено / загружается / доступно
ko 한국어 загружено / загружается / доступно
pl Polski загружено / загружается / доступно
pt Português (Brasil) загружено / загружается / доступно
ru Русский загружено / загружается / доступно
tr Türkçe загружено / загружается / доступно
zh 中文 загружено / загружается / доступно

Каждая строка должна содержать иконку статуса с доступным текстовым описанием и, при необходимости, action:

Состояние Иконка/визуальный статус Поведение
Загружено check/успешный статус Показать размер, версию и action Удалить или Проверить; пакет доступен для dictionary lookup
Загружается spinner/progress Показать прогресс конкретного пакета, отключить повторный запуск той же загрузки
Доступно download/status icon Показать размер и action Скачать
Ошибка warning/error icon Показать причину и action Повторить
Повреждено/неполно repair icon Предложить повторную загрузку или проверку целостности

Нажатие на строку загруженного языка должно открывать детали пакета: название, языковой код, размер, версия, дата установки, поддерживаемые направления lookup и действия Проверить, Обновить, Удалить. Удаление требует подтверждения и не должно удалять пользовательские переводы, цитаты или историю — только локальный dictionary asset.

Состояния экрана

Экран должен иметь явные состояния Loading catalog, Ready, Downloading all, Downloading single, Partial failure, All downloaded, Insufficient storage, Offline, Corrupted asset и Fatal error. При отсутствии сети или невозможности доступа к assets экран не должен падать; локально уже загруженные словари должны продолжать работать.

Acceptance criteria для «Перевод → Словари»

  • В подменю отображается карточка «Словари» с количеством доступных 10 и динамическим количеством загруженных N.
  • Отображается карточка «Скачать все словари» с ориентировочным объёмом около 750 МБ.
  • При массовой загрузке отображается общий progress bar и текущий пакет.
  • Массовая загрузка поддерживает pause/resume/cancel либо, если платформа не позволяет pause, корректный cancel/resume с последнего полного пакета.
  • Повторный запуск не скачивает повторно уже валидные пакеты.
  • После частичной ошибки пользователь видит успешные и неуспешные пакеты и может нажать Повторить.
  • В списке присутствуют ровно 10 кодов: en, fr, it, ja, ko, pl, pt, ru, tr, zh.
  • Для каждого языка отображается один из статусов загружено, загружается, доступно, а также понятные ошибка/повреждено при необходимости.
  • Статус строки согласован с фактическим состоянием DictionaryAssetCatalog и локального хранилища.
  • После успешной загрузки dictionary lookup может использовать пакет без перезапуска приложения.
  • Нажатие «Словари» и любые состояния загрузки не приводят к падению приложения.
  • UI корректно работает при large font, screen reader и недоступном network.

Предлагаемые code pointers: DictionaryAssetCatalog.kt, DictionaryAssetExtractor.kt, DictionaryDownloader.kt, DictionaryRepository.kt, DictionaryDatabase.kt, DictionaryDao.kt, feature-settings Translation screen и локализованные строки core-ui/.../AppStrings.kt.

6. Explain

Для слова/фразы/короткого контекста добавить Explain в reader menu. Результат должен хранить исходный текст, контекст, язык и backend. Local Explain должен работать без внешнего провайдера, если локальная модель доступна. Advanced Explain должен быть выключаемым и не вызывать сеть без явного включения online transport.

Нужно ограничить размер контекста, отменять устаревший запрос при новом выделении, показывать loading/error/retry и не смешивать Explain с обычным переводом. Сводка главы/книги должна быть отдельным batch-сценарием и не запускаться при обычном нажатии «Объяснить».

7. Адаптивное меню выделения

Текущий reader menu демонстрирует действия «Перевести», «Словарь», «Объяснить», «Сохранить цитату», «Подсветить» и «Перевести главу». Его следует превратить в контекстную модель действий:

Контекст Первичные действия Дополнительные действия
Одно слово Словарь, Перевести, Объяснить Копировать, Подсветить, Цитата
Фраза Перевести, Объяснить, Сохранить цитату Словарь, Подсветить, Копировать
Большой диапазон Перевести, Сохранить цитату Объяснить с ограниченным контекстом, Копировать
OCR-блок комикса Перевести блок, Объяснить, Повторить OCR Скрыть overlay, Копировать оригинал
Вся глава Перевести главу Сводка, Отмена, Открыть сохранённый перевод

Reader preset должен влиять на presentation: compact/expanded sheet, порядок вторичных действий, размеры touch target и светлый/тёмный стиль. Он не должен менять доменную семантику или отключать базовые действия без явного правила.

Необходимо отдельно добавить управление существующим highlight. В ReaderHighlightController уже есть deleteHighlight(id), но в ReaderSelectionSheets.kt отсутствует пользовательский callback/action для выбора существующей пометки и удаления. Нужно связать persisted highlight с UI, добавить удаление, изменение цвета и, если поддерживается моделью, заметку.

8. История и хранилище переводов

Расширить существующие TranslationCacheEntry, TranslationCacheDao и RoomTranslationCacheRepository до полноценной истории:

TranslationRecord
- id
- bookId / chapterId / pageId / blockId nullable
- sourceType: TEXT | COMIC
- contextType: WORD | PHRASE | CHAPTER | OCR_BLOCK | PAGE
- sourceText
- translatedText nullable
- sourceLanguage nullable
- targetLanguage
- providerId / transport
- status: SUCCESS | PARTIAL | ERROR | DELETED
- sourceHash / cacheKey
- bbox/geometry nullable for comic blocks
- createdAt / updatedAt

Экран «Перевод» должен поддерживать сортировку по дате, книге, языковой паре и типу результата, поиск, открытие контекста, повтор перевода, удаление результата и очистку истории. Для комиксов запись должна открывать страницу с соответствующим overlay/block, а для текста — reader с исходным диапазоном или chapter context.

9. Настройки Translation и AI

Сохранить разделение, показанное на экранах:

Раздел Ответственность
Перевод Source/target language, режим OFF/OCR+MT/DICTIONARY, OCR filters, overlay opacity/font/style
Сервисы reader Доступ к actions, Explain/lookup/highlight behavior, autoscroll и быстрые reader controls
Словари Installed/downloadable packages, language pairs, priority, fallback
Искусственный интеллект Local/online transport, provider, model, API credentials, Explain, summary, routing
Аудио TTS provider и голосовые параметры

Кнопка перехода из Translation/Services в AI должна вести на корректный экран без дублирования состояния. API keys должны храниться локально защищённо; в UI показывать только masked value и статус подключения. Незаполненный provider не должен приводить к crash.

Acceptance criteria

Text translation

  • Выделенное слово переводится через словарь, если найден результат, и предлагает MT при отсутствии результата.
  • Выделенная фраза переводится через выбранный transport с auto-detect или ручным source language.
  • Перевод главы выполняется через очередь с прогрессом, отменой, retry и partial result.
  • Результаты переживают закрытие и повторное открытие приложения.
  • Повторный запрос использует cache при неизменных source hash, языках, контексте и engine version.
  • UI не зависает на network/model operation и показывает локализованные Loading/Error/Unavailable states.

Comic translation

  • Страница комикса распознаётся на блоки с текстом, bounding box, confidence и типом блока.
  • Фильтры «только диалоги» и «включать SFX» применяются только к автоматическому page-level pipeline.
  • Отдельный tap по OCR-блоку позволяет принудительно повторить OCR/перевод.
  • Перевод блоков отображается overlay поверх оригинала без destructive image edit.
  • Overlay корректно масштабируется при zoom, смене viewport и повторном открытии страницы.
  • Настройки opacity, font size и light/dark/auto style применяются без повторного OCR.
  • Частично переведённая страница показывает готовые блоки и состояние незавершённых блоков.

Dictionaries

  • Кнопка «Словари» не приводит к падению приложения.
  • Пользователь видит установленные, доступные для скачивания и недоступные пакеты.
  • Lookup корректно работает для поддерживаемых языковых пар и не падает при отсутствии пакета.
  • После отсутствия результата доступен переход к машинному переводу.
  • Словарный результат показывает источник и сохраняется в историю только по явному действию либо по согласованной политике.

Explain

  • Explain работает для слова, фразы и короткого контекста.
  • Local Explain не требует сети.
  • Advanced Explain и online provider не вызываются, если отключены в AI settings.
  • Новый selection отменяет/заменяет устаревший Explain request.
  • Ошибки провайдера, лимиты и отсутствие credentials отображаются inline с Retry/Settings action.

Reader menu and highlights

  • Меню адаптируется к word/phrase/large selection/OCR block/chapter.
  • Reader preset изменяет layout/presentation, но не ломает доступность базовых действий.
  • Translation, Dictionary, Explain, Quote, Highlight и Copy имеют единые callbacks и state handling.
  • Существующий цветной highlight можно выбрать и удалить из пользовательского UI.
  • Удалённый highlight исчезает из WebView/overlay и не возвращается после reopen.
  • Изменение цвета не создаёт duplicate highlight и не удаляет цитату.

Translation history

  • История разделяет текстовые и комиксные результаты.
  • Доступны сортировка, поиск, повтор, открытие контекста и удаление.
  • Удаление истории не удаляет исходную книгу, страницу или цитату.
  • Cache key предотвращает смешивание разных языковых пар, providers и prompt versions.

Settings and safety

  • Translation и AI settings не дублируют друг друга.
  • OFF, DICTIONARY, OCR+MT, local и online routes явно отображают текущий effective route.
  • При одинаковых source/target, отсутствии сети, отсутствии словаря, отсутствии модели и отсутствии credentials показывается объяснимое состояние.
  • API keys хранятся защищённо и не попадают в logs, database export или crash reports.
  • Все новые строки добавлены в локализацию минимум для русского и английского языков.

План реализации

Этап 1 — стабилизация существующего foundation

  • Исправить crash при открытии «Словари».
  • Провести аудит TranslationCacheDao/repository и добавить уникальный cache key.
  • Зафиксировать единые TranslationResult, TranslationError и route state.
  • Добавить тесты dictionary availability, missing package, source/target normalization и fallback.

Этап 2 — выделенный текст и reader menu

  • Ввести контекстную action model для selection menu.
  • Подключить Translate/Dictionary/Explain/Quote/Highlight к единому state machine.
  • Добавить UI selection существующего highlight и delete/update color callbacks.
  • Добавить loading, retry, cancellation и stale-request protection.
  • Покрыть меню тестами для word/phrase/large selection/OCR block/chapter.

Этап 3 — локальный и онлайн-перевод текста

  • Подключить local MT engine через существующий selector.
  • Подключить online providers через configured transport и provider policy.
  • Реализовать batch chapter translation queue.
  • Сохранять успешные и partial results в translation history.

Этап 4 — OCR и перевод комиксов

  • Нормализовать OCR block model и reading order.
  • Довести фильтры dialogue/SFX/narrative/unknown.
  • Реализовать page/block translation с partial states.
  • Реализовать overlay renderer с opacity/font/style и viewport scaling.
  • Добавить page-level cache и повторное открытие результата.

Этап 5 — словари, Explain и настройки AI

  • Довести Dictionary screen, package state и priority/fallback.
  • Связать Local Explain и Advanced Explain с AI settings.
  • Подключить OpenRouter/external provider configuration с masked credentials.
  • Реализовать summary как отдельный batch flow.
  • Удалить дублирование настроек между Translation, Services и AI.

Этап 6 — экран истории переводов и полировка

  • Добавить список Translation records с сортировкой и фильтрами.
  • Реализовать open/retry/delete/retranslate.
  • Добавить локализацию, accessibility labels и large-font layout.
  • Провести regression matrix для всех reader presets, форматов, языков и transports.

Тестовая матрица

Ось Значения
Источник EPUB/FB2/MOBI/RTF/DOCX/HTML/TXT/Markdown, PDF/CBZ/CBR/DJVU
Контекст word, phrase, large selection, chapter, OCR block, comic page
Source language auto, manual supported, unsupported/unknown
Target language application language, manual supported, same as source
Transport local, online, auto, unavailable
Dictionary installed, downloading, missing, corrupted, no result
Provider local, Google/Yandex/DeepL/OpenRouter where configured, invalid credentials
Network online, offline, timeout, rate limit
Reader presentation compact/expanded, light/dark, different reader presets, text/comic
Lifecycle first run, reopen, rotate/resize, cancel, retry, app restart

Минимальный набор regression tests должен включать unit-тесты routing/cache/dictionary, ViewModel-тесты cancellation/error/partial states, Compose/UI-тесты меню и settings navigation, а также integration-тесты OCR block → translation → overlay → persistence.

Риски и решения

Риск Решение
OCR неверно распознаёт вертикальный или стилизованный текст Сохранять confidence, позволять ручной повторный OCR и редактирование source text перед переводом
Online provider недоступен или ограничивает запросы Queue, retry/backoff, local fallback, partial result и понятный статус
Перевод большого комикса блокирует UI Page/block jobs, cancellation, cache и foreground progress
Неправильные координаты overlay при zoom Координаты в нормализованной системе страницы, renderer получает viewport transform
Смешивание словаря и MT Явный TranslationContext и route state; dictionary result не маскировать под MT
Утечка API key Encrypted local storage, masked UI, redaction logs и отсутствие ключей в export
Дублирование настроек Translation/AI Один source of truth для TranslationServiceConfig и read-only summary в соседних разделах
Большое адаптивное меню плохо работает на маленьких экранах Приоритетные действия в первом слое, остальные в overflow/bottom sheet, UI tests для large font

Definition of Done

  • Все acceptance criteria выполнены.
  • Добавлены unit, ViewModel, UI и integration tests для новых маршрутов.
  • Нет падения при открытии «Словари», AI settings или Translation history.
  • Нет network call без явного разрешения online transport.
  • Переводы и OCR results восстанавливаются после перезапуска приложения.
  • Удаление highlight работает из пользовательского интерфейса и не удаляет цитаты.
  • У комикса оригинал остаётся неизменным, overlay можно скрыть и повторно показать.
  • Локализация и accessibility проверены.
  • В документации описаны provider setup, dictionary packages, cache policy и privacy behavior.

Связанные файлы репозитория

  • android/core-domain/src/main/java/io/leostrange/mrcomic/core/domain/translation/
  • android/core-data/src/main/java/io/leostrange/mrcomic/core/data/dictionary/
  • android/core-data/src/main/java/io/leostrange/mrcomic/core/data/db/TranslationCacheDao.kt
  • android/core-data/src/main/java/io/leostrange/mrcomic/core/data/db/entity/TranslationCacheEntry.kt
  • android/feature-ocr/src/main/java/io/leostrange/mrcomic/feature/ocr/data/ComicTranslationPipeline.kt
  • android/feature-ocr/src/main/java/io/leostrange/mrcomic/feature/ocr/data/DefaultComicTranslationEngine.kt
  • android/feature-ocr/src/main/java/io/leostrange/mrcomic/feature/ocr/ui/
  • android/feature-reader/src/main/java/io/leostrange/mrcomic/feature/reader/ui/ReaderSelectionSheets.kt
  • android/feature-reader/src/main/java/io/leostrange/mrcomic/feature/reader/ui/ReaderSelectedTextTranslationController.kt
  • android/feature-reader/src/main/java/io/leostrange/mrcomic/feature/reader/ui/ReaderExplainController.kt
  • android/feature-reader/src/main/java/io/leostrange/mrcomic/feature/reader/ui/ReaderHighlightController.kt
  • android/feature-reader/src/main/java/io/leostrange/mrcomic/feature/reader/ui/ReaderTranslationSheets.kt
  • android/feature-settings/src/main/java/io/leostrange/mrcomic/feature/settings/ui/
  • android/engine-llm/src/main/java/io/leostrange/mrcomic/engine/llm/nllb/NllbTranslatorEngine.kt

Источники требований

  • Прикреплённые экраны reader selection menu, Translation settings, OCR input, Overlay, Services и Artificial Intelligence.
  • Текущий checkout репозитория Mr.Comic_fresh_clone, включая существующие translation/OCR/dictionary/AI модули.

Implementation plan and effort estimate

The recommended implementation order is to stabilize the dictionary foundation first, then connect lookup/fallback and AI routing, and only afterwards expand the shared contracts into reader actions, translation history and comic OCR overlay. This avoids parallel implementations of translation results, errors, cache keys and provider state.

Phase Scope Estimate
0 Baseline, reproduce the Dictionaries crash, audit assets and routes 2–2.5 engineering days
1 Dictionary catalog, 10 language packages, single/bulk download, progress and retry 7–10 engineering days
2 Dictionary lookup, normalization, language-pair handling and fallback 6.5–8.5 engineering days
3 Local/online translation routing, provider policy and Explain 8.5–11.5 engineering days
4 Contextual reader menu, Explain state and highlight CRUD 7–10 engineering days
5 Translation cache/history, sorting, reopen, retry and delete 5–7 engineering days
6 OCR block translation and comic overlay geometry 12.5–18.5 engineering days
Total production scope 48.5–68 engineering days

The first usable slice should be limited to baseline plus dictionary catalog/download and one-word lookup: 15.5–21 engineering days for one Android/Kotlin developer. The estimate assumes the existing repository foundation is reused and includes tests and integration work; it is not a promise of calendar time.

First milestone / PR #1

The first PR should implement the dictionary foundation and establish the contract for later AI features:

  • Fix the crash when opening Translation → Dictionaries.
  • Add catalog state for exactly en, fr, it, ja, ko, pl, pt, ru, tr, zh.
  • Show summary 10 available / N installed.
  • Add single-package and bulk download for approximately 750 MB with progress, cancel/resume and partial-failure retry.
  • Skip valid installed packages on repeated download.
  • Add AVAILABLE, DOWNLOADING, INSTALLED, FAILED, CORRUPTED and OFFLINE states.
  • Add typed dictionary lookup request/result/error states for exact match, normalized match, phrase/no-result and missing package.
  • Connect one-word lookup to the reader selection menu.
  • Ensure dictionary route does not call an online provider unless online transport is explicitly allowed.
  • Add unit/UI regression tests for crash, offline, corrupt asset, insufficient storage, partial failure and reader lookup.

Target contracts

The shared layer should expose a context-aware translation request/result contract with contexts such as WORD, PHRASE, CHAPTER, OCR_BLOCK, COMIC_PAGE, EXPLAIN and SUMMARY. Every route must return typed Loading, Success, Partial, Unavailable or Error states. Cache keys must include source hash, language pair, context, provider/transport and engine or prompt version.

Definition of Done for the first PR

The Dictionaries screen opens without a crash in all catalog and download states. All ten language packages are represented and their status comes from the actual catalog/extractor/storage state. Bulk download is idempotent and resumable, and partial failures remain visible. One-word lookup works from the reader menu, missing packages return a recoverable typed error, and no unexpected network request is made. Unit and UI tests pass, strings are localized, accessibility labels are present, and no API credentials are logged or exposed.

Follow-up order

  1. Dictionary lookup and local/online fallback.
  2. Local AI routing and Explain with cancellation and provider error handling.
  3. Contextual reader menu and highlight create/update/delete.
  4. Translation history and cache UI.
  5. OCR block translation and comic overlay.

Architecture addendum: offline packages, AI API and six reader presets

This section turns the scope above into non-negotiable implementation constraints. The detailed standalone architecture package is maintained separately; the decisions below are the acceptance criteria for this issue.

Offline dictionary packages and cache boundaries

Dictionary packages are versioned local artifacts, not ordinary translation-cache rows. The existing DictionaryAssetCatalog, DictionaryAssetExtractor, DictionaryDownloader, DictionaryRepository, DictionaryDatabase and DictionaryDao should be extended through a dedicated Room-backed package registry.

Layer Responsibility Invalidation / recovery
Package registry Actual state, version, source, size, hash, work id, last error and install path for each language. Reconcile filesystem on startup; registry is the source of truth for UI.
Package filesystem Versioned read-only dictionary DB under app filesDir; staging, backup and quarantine folders. Staging is never published; previous valid version remains usable during failed update.
In-memory lookup cache Short TTL/LRU results keyed by language, normalized query, lookup mode and package version. Clear on install/update/remove or TTL expiry.
Translation/Explain cache Result text keyed by source hash, language pair, context, transport, provider, model/engine and prompt version. Invalidate on any input/version mismatch or explicit user delete.
OCR cache Blocks, normalized geometry and translated block states per page revision. Invalidate on image/OCR config/model revision; independent of reader preset colors.

Package lifecycle is: AVAILABLE → QUEUED → DOWNLOADING → VERIFYING → INSTALLING → INSTALLED, with recoverable PAUSED, FAILED, CORRUPTED, UPDATE_AVAILABLE, REMOVING and UNAVAILABLE states. Use resumable versioned .part files, HTTP range/ETag when available, gzip integrity checks, SHA-256 of the extracted database, lightweight DB validation and atomic publish. A package becomes INSTALLED only after a final marker file and a registry transaction succeed.

The package screen must never infer state solely from file presence. In particular, interrupted download/extraction may not replace a prior usable dictionary. Bulk download must skip valid installed packages, retain partial failures, retry only failed packages and preserve offline access for completed packages.

Public domain API requirements

The public domain contracts must use typed state rather than null or raw exceptions. The minimum shared contexts are WORD, PHRASE, SELECTION, CHAPTER, BOOK_SEGMENT, OCR_BLOCK, COMIC_PAGE, EXPLAIN and SUMMARY.

sealed interface OperationState<out T> {
    data object Idle : OperationState<Nothing>
    data class Loading(val operationId: OperationId, val phase: OperationPhase, val progress: Progress? = null) : OperationState<Nothing>
    data class Success<T>(val value: T) : OperationState<T>
    data class Partial<T>(val value: T, val failures: List<OperationFailure>) : OperationState<T>
    data class Unavailable(val reason: UnavailableReason) : OperationState<Nothing>
    data class Error(val failure: OperationFailure) : OperationState<Nothing>
    data object Cancelled : OperationState<Nothing>
}

DictionaryPackageService must expose catalog observation plus enqueue/pause/resume/cancel/retry/verify/remove operations. DictionaryLookupService must distinguish exact, normalized, inflection and phrase matches from NoMatch, missing, unsupported and corrupted packages. TranslationOrchestrator must resolve a route before any provider request and support cancellation/retry. ExplainService must be local-first and only call an external provider after explicit permission.

Routing policy is mandatory: one word or supported short phrase uses dictionary first; fallback order is local MT and then online MT only when enabled. New selection cancels obsolete translation/Explain work. Chapter/book and OCR operations are durable WorkManager-backed jobs with checkpoint/partial retry, not UI-bound coroutines.

Six-preset contract for text and graphic readers

The reader has six presets: Бумага, Сепия, Газета, Ночная тушь, OLED Black and E-Ink. One ReaderPreset / ReaderThemeTokens source must be consumed by both text reader and graphic reader.

The following token group must propagate together: contentSurface, chromeSurface, controlSurface, primaryText, secondaryText, divider, accent, selection, scrim, overlayBubble, errorSurface, toolbar opacity and blur policy.

This applies to text content/chrome, reader control center, contextual selection menu, dictionary/translation/Explain sheets, highlight controls, chapter sheet, graphic reader chrome, OCR translated bubbles, selected OCR border, OCR action menu, sliders, chips, progress and recoverable error sheets. There must be no hardcoded generic charcoal overlay for graphic reader. The comic bitmap itself remains unchanged by a preset; only chrome and overlay adapt unless the user explicitly enables a separate image filter.

Changing a preset while the reader is open must preserve page, scroll, zoom, cached OCR geometry and pending translation job. E-Ink disables blur and uses opaque low-saturation surfaces. Night Ink and OLED Black must not alias the same palette.

Test gates for the six presets

At minimum, maintain seven golden/UI scenes for every preset (42 baseline screens): normal text reader/chrome; control center; selected word menu; dictionary/translation/Explain sheet; existing highlight edit/delete menu; graphic reader with OCR overlay; recoverable error sheet.

Required test classes include token-mapping and persistence unit tests, Compose UI tests, golden screenshot tests, instrumented process recreation/rotation tests and manual accessibility checks. Each preset must be tested for text reader and graphic reader separately. Golden or UI test failure caused by a component retaining a previous preset or using fixed colors is release-blocking.

Security, privacy, performance and rollout gates

API secrets are held only in Keystore-backed encrypted storage and must never enter Room, caches, logs, screenshots or diagnostics. Online requests require an explicit policy check before a provider is created. Technical diagnostics may contain timing, state and error code, but never raw selected text, OCR content, credentials or user file paths.

Use WorkManager for package download, chapter jobs, OCR batch translation and maintenance. Limit default bulk download concurrency to one package. Keep dictionary DB opening and lookup off the main thread; cancel stale selection work. Persist OCR geometry in normalized page coordinates, never in viewport pixels.

Ship behind flags for package registry v2, translation orchestrator v2, shared reader theme propagation, OCR overlay v2 and online providers. Roll out in order: dictionaries/lookup → reader theme propagation → history/chapter jobs → OCR overlay → opt-in online providers. Every flag needs a non-destructive rollback path.

Additional Definition of Done

  • A valid installed dictionary remains usable offline during a failed update or interrupted download.
  • No incomplete/corrupted package can be opened by DictionaryRepository.
  • Cache keys include context, source/target languages, provider, model/engine and prompt version; they never include credentials.
  • All domain-facing operations expose typed Loading/Success/Partial/Unavailable/Error/Cancelled states.
  • All six presets are visibly distinct and applied to every translation-related reader surface in text and graphic modes.
  • Switching preset does not reset reader state or invalidate valid OCR/translation cache.
  • UI and integration tests cover offline, corrupt package, insufficient storage, network disabled, provider timeout/rate limit, missing credentials and all six presets.
  • API keys and user text are redacted from logs, database entries and diagnostic export.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcodexenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions