# Мобильные клавиатуры (iOS + Android) — план, объём, скользящий ввод

> Отдельный этап. Цель — настоящий комфортный ввод новой эрзянской латиницы на телефоне и
> планшете, которого обходные пути (долгое нажатие + системная автозамена) не дают.
> Связано: [[keymap.json]] (та же логика «диакритика + буква»), `scripts/valks_conv.py`
> (правила орфографии), словарь `data/valks_glosses.json` (25 741 лемма) — ключевой актив.

## Главная идея: проблему решает не раскладка, а СЛОВАРЬ

На своей клавиатуре мы владеем всем конвейером ввода, поэтому главный инструмент — не долгое
нажатие каждой спецбуквы, а **восстановление диакритики по нашему словарю** (как у вьетнамских/
чешских клавиатур): пользователь печатает быстро ПЛОСКИМИ латинскими буквами (`melse`,
`sumbra`), клавиатура подставляет правильную орфографию (`meľsë`, `šumbra`).

**Почему это работает — измерено на 25 741 лемме:**
- спецбуквы есть у **66%** слов (то есть долгое нажатие тормозило бы две трети слов);
- но **97.0% «скелетов»** (слово, набранное без диакритики) **однозначны** — ровно одно слово;
- неоднозначны лишь **3.0%** скелетов (5.9% слов): `kus → kus / kuś / kuš`, `car → caŕ / ćar /
  čar` и т. п. — для них показываем короткую ленту кандидатов, ранжированную по частоте.

Итог: **печатаешь плоскими буквами → корректное слово подставляется само**, иначе — один тап по
кандидату. Это и есть «решает ли автозамена проблему»: да, на собственной клавиатуре — решает, в
отличие от системной «Замены текста», которая срабатывает только у границы слова и не строит слово
вживую.

## Что показал офлайн-прототип (измерено, не оценка)

Собрана «общая основа» и проверена на корпусе (скрипты: `kbd_build.py` — словарь,
`kbd_restore.py` — восстановление плоский→орфография, `kbd_eval.py` — метрики). Словарь: **33 389
словоформ** (леммы + варианты + new-формы поиска), 28 609 скелетов. Честные числа:

- **E1 — авто без выбора:** **84.8% скелетов однозначны** (72.7% всех словоформ) → подставляются
  сразу, ноль тапов. (Прежняя оценка «97%» считалась по РАЗЛИЧНЫМ леммам и была оптимистична; по
  всем словоформам, которые реально набирают, честнее **~85% скелетов / 73% слов**.)
- **E2 — лента кандидатов:** у 91.7% неоднозначных всего 2 кандидата; **98.6% помещаются в ленту из
  3** → выбор дёшев.
- **E3 — НОВЫЕ слова (held-out 20%):** по-настоящему незнакомое слово ASCII-набором не
  восстановить (мягкость потеряна). Из таких: **78% проходят плоским текстом** (ошибка ВИДНА, легко
  поправить вручную), 22% дают коллизию скелета — но **98.9% коллизий отличаются лишь диакритикой**
  (обычно допустимый вариант написания того же слова); тихо подставить реально ЧУЖОЕ слово — лишь
  **~0.2%**. То есть опасных скрытых ошибок почти нет.

**Вывод прототипа (до вложений в приложения):** для слов В словаре автокоррекция отличная
(85% — ноль тапов, остальное — 1 тап из 2–3). Главный рычаг качества — **охват словаря**: эрзянский
агглютинативный, поэтому добавление полных словоизменительных форм заметно поднимет E1 и снизит OOV.
По-настоящему новые слова (имена, неологизмы) всё равно требуют ручной диакритики (долгое нажатие) —
это потолок ASCII-ввода, а не нашей реализации. Прокси-вес кандидатов стоит заменить корпусной
частотой (нужен текстовый корпус) — тогда top-1 на неоднозначных станет точнее.

## Расширение охвата — что сделано и где потолок (измерено)

Собран **засвидетельствованный** харвест диакритич. словоформ из наших данных (НЕ выдуманных):
эрз-глоссы `значение_new` (совр. валидированный слой) + проза грамматики Назаренко (`gram_lat.html`),
фильтр «токен с ≥1 спецбуквой» (нужны только формы, которым есть что восстанавливать; заодно отсекает
англ./имена). Реализовано в `kbd_build.harvest_attested()` (приоритет НИЖЕ лемм/вариантов, чтобы леммы
оставались top-1). Результат — **честно небольшой**: +689 форм / +644 скелета, **E1 84.8→85.0%
скелетов (72.7→73.0% слов)**, E2 без изменений (98.6% в ленту из 3). Причина скромного прироста:
наш словарь УЖЕ хорошо покрывает засвидетельствованную лексику — полных словоизменительных парадигм в
наших данных просто НЕТ.

**Большой прирост охвата требует морфологического генератора, а не харвеста.** Руками писать эрзянское
словоизменение нельзя (фабрикация неверных форм → клавиатура «восстановит» в несуществующее слово,
против правила «только по источникам»). Правильный путь — **открытый FST GiellaLT `myv`** (Rueter и
др.; та же линия, что оцифровка Паасонена, которую мы уже используем): сгенерировать валидированные
парадигмы для наших лемм → конвертировать в новую графику (`valks_conv`) → дедуп → влить как формы.
Это отдельная инженерная задача (поставить hfst/foma + FST `myv`, прогнать генерацию), а не разовый
скрипт. Ожидаемо поднимет E1 кратно и снизит OOV на реальном словоизменённом тексте.

**FST ВЫПОЛНЕН и влит (2026-06-21) — см. [`fst/README.md`](fst/README.md).** В WSL без sudo подняты
pyhfst + пребилт-FST `myv`; покрытие FST = 76% лемм. Полный прогон (3.5 мин): 16 126 лемм → сгенерено
и сконвертировано в новую графику **104 182 новые формы** (`fst/fst_forms_new.txt`), влиты в
`kbd_build` источником `fst` (приоритет ниже лемм). **РЕЗУЛЬТАТ ПЕРЕМЕРЕН:** словарь 34k → **138 260
форм**; E1 авто-без-выбора **73.0% → 89.2% слов** (94.5% скелетов); неоднозначных скелетов 15% → 5.5%;
лента ≤3 покрывает 98.6%. Экспорт для разработчика обновлён (`export/erzya_wordlist.tsv`, 138k слов).
**Остаётся (QA):** выверка носителем выборки FST-форм (FST местами over-генерит редкие ячейки;
конвертация инфлектированных форм в графику массово носителем не проверялась — только заголовки);
24% непокрытых FST лемм (диал./Паасонен) охвата не получили. FST в git не кладём (97 МБ).

**Оценка трудозатрат (среда проверена 2026-06-21):** FST-инструментов нет (ни hfst, ни foma), но WSL
есть → Linux-путь установки доступен; cyr→new конвертация уже готова и выверена (самое сложное снято);
кириллич. лемм для входа 25 821, но требуют чистки (фразы/фрагменты). Сложность — **средняя**
(интеграция+лингвонастройка+среда, не алгоритмика). Сроки: **оптимистично ~2–3 дня** (пребилт-FST,
ядро парадигм), **реалистично ~1 неделя**, с невезением (сборка из исходников giella-core, дыры FST) —
**до ~2 недель**; плюс async-циклы выверки носителем. Гл. риски: сборка FST на WSL (если есть пребилт-
генератор `myv` — схлопывается до часов), покрытие FST (Паасонен-историч./диал. леммы он не знает →
прирост по стандартной совр. лексике), взрыв объёма (объектное спряжение/посессив — скоупить ядро).
**ВАЖНО — НЕ блокер:** FST не обязан идти ПЕРЕД приложениями. Лучшая последовательность — сделать
Android-клавиатуру на текущем словаре (85% — сильный UX, свайп тоже опирается на словарь), а
FST-расширение **влить позже как обновление данных** (больший словарь, без изменения кода приложения).
Так FST — параллельная/последующая дата-задача, не критический путь; риск сборки не задерживает запуск.

Подстраховка для слов вне словаря (имена, новые термины): дедуктивные правила из `valks_conv`
(палатализация предсказуема) + ручной ввод диакритики дедицированным рядом/долгим нажатием.

## Скользящий ввод (glide / swipe) — как именно

Свайп ведёт палец по **базовым** буквам (a–z). Декодер сопоставляет траекторию со словами
словаря. Диакритика для свайпа — не препятствие, а наоборот: записи словаря УЖЕ несут диакритику,
а их базовый скелет совпадает с траекторией. Значит:

> ведёшь палец по скелету `m-e-l-s-e` → декодер возвращает словарное `meľsë` с уже
> восстановленной диакритикой.

То есть свайп и «диакритик-восстановление» — это одно и то же по механике, просто ввод траекторией
вместо тапов. Нужен (1) glide-декодёр путь→слово и (2) словарь с частотами — **второе у нас есть**.

**Доступность по платформам:**
- **Android — реально.** Своего glide-движка ОС не даёт, но есть открытые: **FlorisBoard**
  (Apache-2.0, кастомные раскладки пакетами-расширениями, glide в развитии) и AnySoftKeyboard
  (gesture typing). Путь — взять открытый IME и скормить ему наш эрзянский словарь.
- **iOS — нереалистично.** Apple НЕ даёт glide-движок кастомным клавиатурам и нет открытого, как
  на Android. Свайп пришлось бы писать с нуля (это ядро Gboard/SwiftKey). Вывод: **iOS — ввод
  тапами с диакритик-автокоррекцией по словарю, без свайпа.**

## Что даёт каждая платформа (и объём работ)

### Android (IME — InputMethodService)
- Платформа открытая. **Рекомендация: не писать с нуля, а взять FlorisBoard** — кастомная
  эрзянская раскладка как extension + наш словарь (формат словаря Floris), suggestions/glide
  переиспользуем. Меньше кода, быстрее результат.
  - Риски: зрелость glide во Floris (проверить актуальное состояние); упаковка нашего словаря в
    его формат; поддержка при обновлениях апстрима.
- Альтернатива (Option B): свой IME с нуля — максимум контроля, кратно больше работы, glide всё
  равно не изобретать (тупик). Не рекомендуется для v1.
- Объём v1 (раскладка + наш словарь + suggestions + тап-ввод диакритики; glide — если движок
  Floris готов): **реалистично 2–4 недели** сфокусированной работы. Алгоритм восстановления и данные
  уже готовы (`kbd_restore.py`, `kbd_dict.json`) — порт на Kotlin, не разработка с нуля.
  **СНАЧАЛА спайк (1–3 дня):** загрузить наш список слов в FlorisBoard/AnySoftKeyboard и проверить,
  складывает ли движок диакритику в подсказках (набрал `melse` → `meľsë`). Если ДА — путь «упаковка»,
  ~1–2 недели до v1; если НЕТ — нужен свой провайдер подсказок, ~3–4 недели (+1–2 на свайп). Этот тест
  снимает главную неопределённость до старта. Свайп — джокер (зрелость glide во Floris); тап-версия от
  него не зависит, свайп можно вторым релизом.
- **Если своего Android-разработчика НЕТ** — отдельный план: [`NO-DEV-PLAN.md`](NO-DEV-PLAN.md)
  (Keyman от SIL — самосбор, Android+iOS, наш словарь плагается; и/или сотрудничество с GiellaLT/Divvun,
  чей FST `myv` мы уже используем).

### iOS (App Extension — Keyboard)
- Apple базовой клавиатуры НЕ даёт: рисуем ВСЁ сами (раскладка, Shift/Caps, Delete с автоповтором,
  «глобус» переключения, Return), своя лента подсказок, **свой автокоррект/диакритик-восстановление
  по нашему словарю** (системный автокоррект кастомным клавиатурам недоступен — но словарь у нас
  есть). Свайпа нет.
- Нужен Apple Developer Program (99 $/год) и ревью App Store.
- Объём v1 (тап + словарная диакритик-автокоррекция + лента кандидатов): **средний–высокий**
  (сравнимо с Android или больше, т.к. всё с нуля; зато без glide).

### Общая основа (делается один раз, переиспользуется обеими)
- **Генератор словаря клавиатуры** из `valks_glosses.json`: список слов + частоты + индекс по
  базовому скелету (для автокоррекции и glide). Данные есть — это скрипт в духе `valks_build.py`.
- **Модель восстановления диакритики**: словарь (97% однозначно) + правила `valks_conv` для слов
  вне словаря + ранжирование кандидатов по частоте.

## Этапы

1. **Основа (общая):** генератор клавиатурного словаря + модель диакритик-восстановления + лента
   кандидатов (прототип-алгоритм можно проверить оффлайн на корпусе ещё до клавиатур).
2. **Android v1 на FlorisBoard:** эрзянская раскладка-расширение + наш словарь + тап-ввод; затем
   подключить glide и suggestions. Бета через APK, потом Google Play.
3. **iOS v1:** keyboard extension (тап + словарная автокоррекция + кандидаты), контейнер-приложение,
   TestFlight → App Store.
4. **Полировка:** долгое нажатие как запасной ручной ввод диакритики, тёмная тема, гаптика,
   планшетный режим (раздельная раскладка), выверка носителем.

## Оценка и ограничения (честно)
- **Сложность:** Android — *умеренная* (стоим на плечах FlorisBoard, glide почти «бесплатно», если
  его движок готов). iOS — *выше* (всё с нуля, без glide). Это разработка приложений (Kotlin + Swift)
  и публикация в двух магазинах с ревью и последующей поддержкой, а не файл-раскладка.
- **Свайп:** будет на Android, на iOS — нет (ограничение платформы). На iOS его компенсирует
  быстрый плоский набор + словарная автокоррекция (тоже почти без долгих нажатий).
- **Аккаунты/деньги:** Apple Developer 99 $/год; Google Play — разовый взнос 25 $.
- **Поддержка:** обновления под новые версии ОС, расширение словаря — постоянная статья.
- **Планшет:** те же приложения работают и на планшете; на Android-планшете с физической
  клавиатурой раскладку можно пробросить через IME; на iPad кастомную аппаратную раскладку ОС не
  принимает (системное ограничение) — там экранная клавиатура с автокоррекцией.

## Рекомендация
Делать **в две дорожки с общей основой**, начать с **Android на базе FlorisBoard** (быстрее даёт
свайп + suggestions на нашем словаре — самый сильный мобильный опыт), параллельно/следом — **iOS
keyboard extension** (тап + словарная автокоррекция). Ключ к комфорту на обеих — наш словарь и
97%-однозначное восстановление диакритики, а не ряд диакритических клавиш.
