7c6b182402
build / build (push) Failing after 10s
Программа накладывает русский перевод прямо на английские надписи — как переводчик по фото, только для монитора. Работает с играми, интерфейсами и документами: распознаёт кадр, собирает текст в связные блоки, переводит и рисует плашки под цвет фона. Распознавание — встроенный Windows.Media.Ocr, офлайн. Перевод — цепочка движков с автопереключением (google, lingva, mymemory), потому что до разных сервисов из разных сетей достучаться получается по-разному. Собирается без .NET SDK: Roslyn из Build Tools плюс сборки Framework 4.8, которые уже есть в системе. На выходе один exe без установщика. Главная сложность оказалась не в переводе, а в том, чтобы понять, где на экране связный текст, а где отдельные надписи. Классические алгоритмы сегментации решают это по межстрочному зазору, но в плотном интерфейсе он одинаков у строк абзаца и у соседних контролов. Поэтому решение принимает ансамбль признаков: фон, граница между строками, заполненность строки, кегль. Надписи на чужом языке отсеиваются тремя уровнями — форма слова, вторая модель распознавания и частоты буквенных пар. Чтобы правки эвристик не оценивались на глаз, в tools лежат эталонные кадры и RenderTest: прогон конвейера без окна с подсчётом попаданий. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
242 lines
18 KiB
Markdown
242 lines
18 KiB
Markdown
# Переводчик экрана
|
||
|
||
Наводит перевод поверх английского текста прямо на экране — как Яндекс.Переводчик по фото,
|
||
только для монитора. Нажал горячую клавишу → английские надписи закрылись плашками
|
||
под цвет фона, на них русский текст. Остальная картинка остаётся на месте.
|
||
|
||
Работает с любой программой: игры, PDF, видео, интерфейсы, картинки — всё, что видно на экране.
|
||
|
||
* Распознавание — встроенный в Windows OCR: офлайн, бесплатно, ничего ставить не надо.
|
||
* Перевод — несколько движков с автоматическим переключением, если какой-то не отвечает.
|
||
* Один файл `ScreenTranslator.exe` (81 КБ), без установщика и без .NET SDK.
|
||
|
||
## Как пользоваться
|
||
|
||
| Клавиши | Действие |
|
||
|---|---|
|
||
| `Ctrl+Alt+Z` | перевести монитор, на котором сейчас курсор |
|
||
| `Ctrl+Alt+X` | выделить область рамкой и перевести её |
|
||
| `Esc` или повторное нажатие | закрыть перевод |
|
||
| Пробел (удерживать) | подсмотреть оригинал под плашками |
|
||
| Наведение мышью | показать оригинал одного фрагмента |
|
||
| Клик по плашке | скопировать её перевод |
|
||
| `Ctrl+C` | скопировать весь перевод |
|
||
|
||
Значок в трее — те же команды плюс «Проверить переводчики» и правка настроек.
|
||
|
||
Пока идёт распознавание и перевод, экран уже заморожен, а фрагменты подсвечены — видно,
|
||
что программа работает. Полный цикл на мониторе 3440×1440 — около трёх секунд
|
||
(0,6 с распознавание + ~2 с перевод), повтор того же текста — мгновенно из кэша.
|
||
|
||
## Установка
|
||
|
||
Скопировать папку `dist` куда угодно и запустить `ScreenTranslator.exe`. Всё.
|
||
|
||
Если Windows покажет «SmartScreen защитил ваш компьютер» — «Подробнее» → «Выполнить в любом случае»
|
||
(файл без цифровой подписи).
|
||
|
||
Автозапуск: `Win+R` → `shell:startup` → положить туда ярлык на exe.
|
||
|
||
### Требование одно: модель распознавания
|
||
|
||
Нужен установленный в Windows языковой компонент «Распознавание текста» для английского.
|
||
Проверить: значок в трее → «Проверить переводчики» покажет ошибку, если моделей нет.
|
||
|
||
Ставится так: **Параметры → Время и язык → Язык и регион** → у английского языка `···` →
|
||
**Компоненты языка** → отметить **Распознавание текста**. На большинстве систем уже стоит.
|
||
|
||
## Настройка
|
||
|
||
Рядом с exe лежит `config.json` (создаётся при первом запуске). После правки —
|
||
«Перечитать настройки» в трее, перезапуск не нужен.
|
||
|
||
Самое полезное:
|
||
|
||
```jsonc
|
||
"hotkeyTranslate": "Ctrl+Alt+Z", // своя комбинация, если эта занята
|
||
"ocrLanguage": "en-US", // язык оригинала на экране
|
||
"targetLang": "ru", // на какой переводить
|
||
"overlayMode": "frozen", // frozen — стоп-кадр; live — плашки поверх живого экрана
|
||
"captureScope": "monitor", // monitor — экран под курсором; all — все мониторы сразу
|
||
"ocrScale": 2.0, // 1.0 быстрее, 2.0 точнее на мелком шрифте
|
||
"detectForeign": true, // не трогать надписи на языке системы (см. ниже)
|
||
"markTranslated": true, // подчёркивать переведённые места
|
||
"chain": ["google", "lingva", "mymemory"]
|
||
```
|
||
|
||
### Смешанный экран
|
||
|
||
Английская модель OCR, встретив русскую надпись, читает её как бессмыслицу вроде `C03AaTb`
|
||
или `Pa3Mep` — и без защиты программа переводила бы эту кашу, залепляя плашками весь
|
||
русский интерфейс.
|
||
|
||
Защита двухступенчатая:
|
||
|
||
1. `skipGarbage` — отсев по форме слов: цифра посреди слова, заглавная после строчной,
|
||
символы-артефакты, имена файлов.
|
||
2. `detectForeign` — кадр дополнительно распознаётся моделью второго языка. Если она видит
|
||
на этом месте кириллицу, надпись изначально русская и её не трогают. Это ещё примерно
|
||
столько же времени на распознавание; выключается, если весь экран и так на одном языке.
|
||
3. Список ходовых английских слов ([EnglishWords.cs](src/EnglishWords.cs)) — арбитр в спорных
|
||
случаях. Русская модель читает латинское «Cancel» как «Сапсеl», то есть по алфавиту оно
|
||
неотличимо от русского «Профиль» → `npocbwlb`. Если основная модель выдала узнаваемое
|
||
английское слово, верим ей.
|
||
|
||
На эталонах это даёт 22 распознанных блока из 22 на английском кадре (ни одной потери)
|
||
и 2 оставшихся из 20 на русском.
|
||
|
||
Совсем идеально не будет: очень мелкие надписи (вкладки браузера, 8 px) обе модели читают
|
||
плохо, и часть их может проскочить. **Если на экране мешанина языков — надёжнее `Ctrl+Alt+X`
|
||
и рамка вокруг нужного куска.**
|
||
|
||
### Чего программа не умеет и не сможет
|
||
|
||
Рабочий стол, панель задач, списки установленных программ — не её сценарий. Подписи там это
|
||
названия приложений, а отличить название от обычного слова невозможно: «Steam», «Discord»,
|
||
«Wand» одновременно и то, и другое. Перевод получится буквальным и бессмысленным.
|
||
|
||
Помогает `"minWords": 2` — тогда переводятся только фразы от двух слов, а одиночные подписи
|
||
остаются как есть. Но за это приходится платить: на интерфейсе игры теряется половина надписей
|
||
(«Mining», «Search», «Consumable» — тоже одиночные слова), поэтому по умолчанию стоит `1`.
|
||
|
||
### Про переводчики и Россию
|
||
|
||
`chain` — это порядок попыток. Первый ответивший движок используется, упавший временно
|
||
исключается и пробуется следующий. Ничего перенастраивать при переезде в другую сеть не нужно —
|
||
если Google недоступен, программа сама уйдёт на запасной.
|
||
|
||
Что стоит знать:
|
||
|
||
* **google** — бесплатный публичный эндпоинт, без ключа, лучшее соотношение качества и скорости.
|
||
`translate.googleapis.com` в России не заблокирован (замедляли YouTube, а не API), но
|
||
у отдельных провайдеров ТСПУ рвёт к нему TLS. Отсюда и нужна цепочка.
|
||
* **lingva** — открытый шлюз к тому же Google на других адресах. Ровно тот случай, когда
|
||
напрямую не пускает, а через шлюз проходит. Медленнее (2–4 с).
|
||
* **mymemory** — последний рубеж, без ключа, лимит около 5000 слов в сутки на IP.
|
||
* **yandex** — самый устойчивый канал внутри РФ, но нужен платный Yandex Cloud
|
||
(`folderId` + API-ключ).
|
||
* **deepl** — лучшее качество en→ru, есть бесплатный тариф с ключом.
|
||
* **llm** — перевод целым экраном через Claude API: лучше держит контекст интерфейса,
|
||
но платный по токенам и медленнее.
|
||
* **libretranslate** / **relay** — свой сервер, ничего не уходит наружу.
|
||
|
||
Проверить, что доступно из конкретной сети: трей → **Проверить переводчики** — покажет
|
||
время ответа или ошибку по каждому.
|
||
|
||
Через прокси (например, свой sing-box в режиме mixed):
|
||
|
||
```jsonc
|
||
"proxy": "http://192.168.5.42:2080"
|
||
```
|
||
|
||
### Свой релей
|
||
|
||
Если публичные сервисы недоступны, а свой сервер есть — поднимите эндпоинт, принимающий
|
||
`POST {"texts": [...], "from": "en", "to": "ru"}` и возвращающий `{"texts": [...]}`
|
||
той же длины, затем:
|
||
|
||
```jsonc
|
||
"chain": ["relay", "google"],
|
||
"engines": { "relay": { "url": "https://ваш-домен/translate" } }
|
||
```
|
||
|
||
Внутри релей может ходить хоть в Google, хоть в локальную модель — клиенту всё равно.
|
||
|
||
## Приватность
|
||
|
||
Распознавание идёт локально, наружу уходит **только распознанный текст** — картинка экрана
|
||
никогда не покидает компьютер. Но текст с экрана всё же уходит в выбранный сервис перевода,
|
||
поэтому для чувствительного содержимого используйте `libretranslate` или `relay` на своём сервере.
|
||
|
||
Кэш переводов лежит рядом в `cache.json` — его можно удалять в любой момент.
|
||
|
||
## Сборка из исходников
|
||
|
||
.NET SDK не нужен — используется Roslyn из Visual Studio Build Tools и сборки .NET Framework 4.8,
|
||
которые уже есть в Windows.
|
||
|
||
```powershell
|
||
.\build.ps1 # собрать в .\dist
|
||
.\build.ps1 -Run # собрать и запустить
|
||
.\build.ps1 -Test # плюс RenderTest.exe
|
||
```
|
||
|
||
`RenderTest.exe` — не сама программа, а диагностика: прогоняет весь конвейер без окна и
|
||
сохраняет две картинки (как OCR разметил блоки и как лёг перевод). Запускать лучше из терминала;
|
||
при двойном клике он всё равно отработает и подождёт Enter, а картинки положит
|
||
в `%TEMP%\screen-translator-test`.
|
||
|
||
```powershell
|
||
.\dist\RenderTest.exe --in кадр.png --out .\dist\test
|
||
.\dist\RenderTest.exe --scale 1.0 --no-translate
|
||
```
|
||
|
||
Два эталонных кадра для прогонов рисуются скриптами:
|
||
|
||
* `tools\make-sample.ps1` — англоязычный интерфейс. Проверка «всё ли переводится»:
|
||
правильный результат — 22 блока.
|
||
* `tools\make-sample-ru.ps1` — русский интерфейс. Негативная проверка: английская модель OCR
|
||
читает кириллицу как кашу, и её нельзя ни переводить, ни закрывать плашками.
|
||
Правильный результат — около 5 блоков (мелкие обрывки вроде «Tn»), было 20 до фильтра.
|
||
|
||
Оба стоит прогонять после правок в `Blocks.cs`: ужесточение фильтра легко начинает резать
|
||
короткие английские кнопки, и английский кадр это сразу показывает.
|
||
|
||
Флаг `--expect файл.txt` считает, сколько заранее известных строк реально распозналось —
|
||
без такой метрики улучшение и ухудшение на глаз выглядят одинаково убедительно.
|
||
`--selftest` проверяет биграммную модель на примерах настоящих надписей и каши.
|
||
|
||
```powershell
|
||
.\dist\RenderTest.exe --in wow.jpg --expect tools\expect-wow.txt --no-translate
|
||
.\dist\RenderTest.exe --selftest
|
||
```
|
||
|
||
Текущие показатели (после каждой правки должны держаться):
|
||
|
||
| Кадр | Метрика | Значение |
|
||
|---|---|---|
|
||
| Скриншот игры | распознано известных надписей | 14 из 14 |
|
||
| Английский эталон | ложно отброшено | 0 |
|
||
| Русский эталон | осталось непойманной каши | 2 из 20 |
|
||
|
||
## Что внутри
|
||
|
||
| Файл | Зачем |
|
||
|---|---|
|
||
| `src/Program.cs` | трей, горячие клавиши, порядок работы |
|
||
| `src/ScreenCapture.cs` | снимок экрана и отдельная копия пикселей для фоновой обработки |
|
||
| `src/Ocr.cs` | Windows.Media.Ocr, увеличение кадра ради мелкого шрифта |
|
||
| `src/Blocks.cs` | склейка строк в абзацы, отсев каши, подбор цветов плашки |
|
||
| `src/Translators.cs` | движки перевода, цепочка фоллбеков, кэш |
|
||
| `src/PlateRenderer.cs` | подбор кегля и отрисовка плашки |
|
||
| `src/OverlayForm.cs` | окно поверх экрана |
|
||
| `src/RegionSelector.cs` | выделение области рамкой |
|
||
|
||
| `src/EnglishBigrams.cs` | частоты буквенных пар: отличает слово от каши распознавания |
|
||
| `src/EnglishWords.cs` | список ходовых английских слов — арбитр в спорных случаях |
|
||
|
||
Откуда взялись решения (короткий разбор чужого опыта):
|
||
|
||
* Классические алгоритмы сегментации — RLSA, Docstrum, XY-cut, Voronoi — принимают решение
|
||
«склеивать или нет» по одному сигналу, межстрочному зазору. В плотном интерфейсе он
|
||
одинаков у строк абзаца и у соседних контролов, поэтому ни один из них эту задачу не решает.
|
||
Работает только ансамбль независимых признаков — как в Tesseract (`paragraphs.h`, геометрия
|
||
плюс пунктуация) и UIED, где графику детектируют отдельно от текста.
|
||
* Отсюда четыре признака разрыва: разный фон, граница между строками, «строка не дотянула
|
||
до правого края» (в абзаце короткой бывает только последняя) и несовпадение кегля.
|
||
* Определение чужого языка — это не задача language identification: готовые детекторы
|
||
(cld3, fastText) всегда выбирают какой-то язык из списка и уверенно зовут кашу английским.
|
||
Нужна одноклассовая модель «похоже на настоящий английский», отсюда биграммы.
|
||
* Проверено и отвергнуто: перевод в оттенки серого и инверсия тёмного фона перед OCR.
|
||
Обещали заметный прирост, на замере не дали ничего (14 из 20 во всех вариантах),
|
||
поэтому по умолчанию выключены, хотя в конфиге остались.
|
||
|
||
Тонкие места, если будете править:
|
||
|
||
* Приложение помечено `PerMonitorV2` — все координаты физические, иначе разметка OCR
|
||
разъезжается с экраном при масштабе интерфейса больше 100 %.
|
||
* OCR обводит только тело букв, без выносных элементов, поэтому плашка расширяется
|
||
по вертикали — иначе сверху и снизу торчат хвосты оригинала.
|
||
* Строки склеиваются в абзац по межстрочному интервалу и одинаковому кеглю: рваные строки
|
||
переводятся заметно хуже целого абзаца.
|