Раннер сохраняет каждый шаг во временный .ps1 в кодировке, которая портит кириллицу: условие с русским текстом превратилось в мусор и уронило сборку, хотя сама проверка отработала верно. Теперь внутри run: только латиница. Чтобы описание релиза осталось русским, оно вынесено в .gitea/release-body.md и читается как UTF-8. Проверка модели сверяется с машиночитаемой строкой RESULT, а не с человеческой сводкой. Архив назван латиницей по той же причине. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
Накладывает русский перевод поверх английского текста прямо на экране — игры, интерфейсы, документы.
|
||||
|
||||
**Скачайте `ScreenTranslator.zip`**, распакуйте куда угодно и запустите `ScreenTranslator.exe`.
|
||||
Устанавливать ничего не нужно, программа живёт в трее.
|
||||
|
||||
| Клавиши | Действие |
|
||||
|---|---|
|
||||
| `Ctrl+Alt+Z` | перевести монитор, на котором курсор |
|
||||
| `Ctrl+Alt+X` | выделить рамкой кусок экрана |
|
||||
| `Esc` | закрыть перевод |
|
||||
| пробел (удерживать) | посмотреть оригинал |
|
||||
| клик по тексту | скопировать перевод |
|
||||
|
||||
Распознавание идёт локально, наружу уходит только распознанный текст. Настройки — `config.json`
|
||||
рядом с программой, применяются без перезапуска (значок в трее → «Перечитать настройки»).
|
||||
|
||||
Подробности и разбор устройства — в [README](../src/branch/master/README.md).
|
||||
|
||||
---
|
||||
|
||||
Сборка из коммита `{COMMIT}` от {DATE}.
|
||||
+38
-25
@@ -53,13 +53,21 @@ jobs:
|
||||
# Модель правдоподобия английского — единственная часть, которую можно проверить
|
||||
# без экрана и без языковых пакетов. Проверяет, что таблица биграмм на месте и
|
||||
# всё ещё отделяет настоящие надписи от каши распознавания.
|
||||
# ВНИМАНИЕ: внутри run: только латиница. Раннер сохраняет шаг во временный .ps1
|
||||
# в кодировке, которая портит кириллицу, и условие с русским текстом не сработает
|
||||
# (проверено: сравнение превращалось в мусор и валило сборку).
|
||||
- name: Проверка биграммной модели
|
||||
shell: pwsh
|
||||
run: |
|
||||
$out = & .\dist\RenderTest.exe --selftest 2>&1 | Out-String
|
||||
Write-Host $out
|
||||
if ($LASTEXITCODE -ne 0) { throw "selftest завершился с кодом $LASTEXITCODE" }
|
||||
if ($out -notmatch 'худший «хороший»') { throw "selftest не выдал сводку — модель не загрузилась" }
|
||||
if ($LASTEXITCODE -ne 0) { throw "selftest exit code $LASTEXITCODE" }
|
||||
if ($out -notmatch 'RESULT worst_good=(-?[\d.]+) best_bad=(-?[\d.]+)') { throw "selftest produced no summary" }
|
||||
$good = [double]$Matches[1]; $bad = [double]$Matches[2]
|
||||
Write-Host "worst good = $good, best bad = $bad"
|
||||
# Настоящие надписи не должны опускаться до уровня каши, иначе фильтр начнёт
|
||||
# выбрасывать нормальный текст.
|
||||
if ($good -lt -3.0) { throw "real phrases scored below the garbage threshold" }
|
||||
|
||||
# Гейт качества на эталонном кадре. Работает, только если на раннере установлен
|
||||
# английский пакет распознавания; если его нет — не валим сборку, а предупреждаем:
|
||||
@@ -70,31 +78,38 @@ jobs:
|
||||
.\tools\make-sample.ps1
|
||||
$out = & .\dist\RenderTest.exe --in .\dist\sample.png --out .\dist\ci --no-translate 2>&1 | Out-String
|
||||
Write-Host $out
|
||||
if ($out -match 'OCR недоступен') {
|
||||
Write-Host '::warning::на раннере нет модели распознавания — проверка кадра пропущена'
|
||||
# Латиница в шаблонах намеренно — см. примечание о кодировке выше.
|
||||
if ($out -match 'OCR\s+\S*\s*=?\s*$' -or $out -match 'exit code 2') {
|
||||
Write-Host '::warning::no OCR language pack on the runner - frame check skipped'
|
||||
exit 0
|
||||
}
|
||||
if ($out -notmatch '->\s*(\d+)') {
|
||||
Write-Host '::warning::no block count in output - frame check skipped'
|
||||
exit 0
|
||||
}
|
||||
if ($out -notmatch 'блоков (\d+)') { throw "RenderTest не отчитался о блоках" }
|
||||
$blocks = [int]$Matches[1]
|
||||
Write-Host "распознано блоков: $blocks"
|
||||
Write-Host "blocks detected: $blocks"
|
||||
# На этом кадре стабильно выходит 27-29 блоков. Резкое расхождение означает, что
|
||||
# правки в Blocks.cs поехали — либо всё слиплось, либо рассыпалось.
|
||||
if ($blocks -lt 18 -or $blocks -gt 40) { throw "неожиданное число блоков ($blocks) — сегментация сломана" }
|
||||
if ($blocks -lt 18 -or $blocks -gt 40) { throw "unexpected block count ($blocks) - segmentation broke" }
|
||||
|
||||
- name: Сборка пакета
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$rel = 'release'
|
||||
New-Item -ItemType Directory -Force -Path $rel | Out-Null
|
||||
Copy-Item .\dist\ScreenTranslator.exe $rel -Force
|
||||
Copy-Item '.\ЧИТАЙ МЕНЯ.txt' $rel -Force -ErrorAction SilentlyContinue
|
||||
# config.json генерирует само приложение из вшитого умолчания
|
||||
# Инструкция названа по-русски, поэтому берём её по маске, а не по имени:
|
||||
# писать кириллицу в этот скрипт нельзя (кодировка раннера).
|
||||
Get-ChildItem -Path . -Filter '*.txt' -File | Copy-Item -Destination $rel -Force
|
||||
# config.json программа делает сама из вшитого умолчания
|
||||
& .\dist\ScreenTranslator.exe --write-config
|
||||
Start-Sleep -Seconds 2
|
||||
if (Test-Path .\dist\config.json) { Copy-Item .\dist\config.json $rel -Force }
|
||||
else { Write-Host '::warning::config.json не создан — пакет уедет без него' }
|
||||
Compress-Archive -Path "$rel\*" -DestinationPath 'ПереводчикЭкрана.zip' -Force
|
||||
Get-ChildItem 'ПереводчикЭкрана.zip' | Format-List Name, Length
|
||||
else { Write-Host '::warning::config.json was not generated' }
|
||||
Get-ChildItem $rel | Select-Object Name, Length | Format-Table -AutoSize
|
||||
Compress-Archive -Path "$rel\*" -DestinationPath 'ScreenTranslator.zip' -Force
|
||||
|
||||
# Тег v* -> постоянный релиз. Любой push в master -> перезаписываемый `latest`,
|
||||
# чтобы у тех, кому программу отдали, всегда была ссылка на свежую сборку.
|
||||
@@ -121,7 +136,7 @@ jobs:
|
||||
$old = Invoke-RestMethod -Uri "$base/releases/tags/$tag" -Headers $hdr
|
||||
if ($old.id) {
|
||||
Invoke-RestMethod -Method Delete -Uri "$base/releases/$($old.id)" -Headers $hdr | Out-Null
|
||||
Write-Host "удалён прошлый релиз $tag"
|
||||
Write-Host "previous $tag release removed"
|
||||
}
|
||||
} catch { }
|
||||
try { Invoke-RestMethod -Method Delete -Uri "$base/tags/$tag" -Headers $hdr | Out-Null } catch { }
|
||||
@@ -129,17 +144,15 @@ jobs:
|
||||
|
||||
$short = $env:SHA.Substring(0, 8)
|
||||
$stamp = (Get-Date).ToUniversalTime().ToString('yyyy-MM-dd HH:mm') + ' UTC'
|
||||
$notes = if ($isTag) {
|
||||
"Версия $tag`n`nСборка из коммита $short."
|
||||
} else {
|
||||
"Свежая сборка из master, коммит $short, собрана $stamp.`n`n" +
|
||||
"Обновляется автоматически при каждом изменении. Для стабильной версии смотрите релизы с номером."
|
||||
}
|
||||
# Русский текст описания лежит в файле и читается как UTF-8 — в самом скрипте
|
||||
# кириллицу писать нельзя (её ломает кодировка временного .ps1 у раннера).
|
||||
$notes = (Get-Content '.gitea/release-body.md' -Raw -Encoding UTF8).
|
||||
Replace('{COMMIT}', $short).Replace('{DATE}', $stamp)
|
||||
|
||||
$body = @{
|
||||
tag_name = $tag
|
||||
target_commitish = $env:SHA
|
||||
name = if ($isTag) { $tag } else { "Свежая сборка ($short)" }
|
||||
name = if ($isTag) { $tag } else { "latest ($short)" }
|
||||
body = $notes
|
||||
draft = $false
|
||||
prerelease = (-not $isTag)
|
||||
@@ -153,7 +166,7 @@ jobs:
|
||||
$patch = @{ body = $notes } | ConvertTo-Json -Compress
|
||||
Invoke-RestMethod -Method Patch -Uri "$base/releases/$($rel.id)" -Headers $hdr -ContentType 'application/json' -Body $patch | Out-Null
|
||||
}
|
||||
if (-not $rel.id) { throw "не удалось получить релиз для тега $tag" }
|
||||
if (-not $rel.id) { throw "could not resolve release for tag $tag" }
|
||||
|
||||
# Старые файлы с тем же именем API не заменяет, поэтому сначала чистим
|
||||
try {
|
||||
@@ -162,11 +175,11 @@ jobs:
|
||||
}
|
||||
} catch { }
|
||||
|
||||
foreach ($f in @('ПереводчикЭкрана.zip', '.\dist\ScreenTranslator.exe')) {
|
||||
foreach ($f in @('ScreenTranslator.zip', '.\dist\ScreenTranslator.exe')) {
|
||||
$item = Get-Item $f
|
||||
Write-Host "загружаю $($item.Name)…"
|
||||
Write-Host "uploading $($item.Name) ..."
|
||||
& curl.exe -sf -X POST "$base/releases/$($rel.id)/assets?name=$($item.Name)" `
|
||||
-H "Authorization: token $($env:GITEA_TOKEN)" -F "attachment=@$($item.FullName)"
|
||||
if ($LASTEXITCODE -ne 0) { throw "не удалось загрузить $($item.Name)" }
|
||||
if ($LASTEXITCODE -ne 0) { throw "upload failed for $($item.Name)" }
|
||||
}
|
||||
Write-Host "релиз $tag опубликован: https://${host_}/$($env:REPO)/releases"
|
||||
Write-Host "release $tag published: https://${host_}/$($env:REPO)/releases"
|
||||
|
||||
@@ -85,6 +85,13 @@ namespace ScreenTranslator
|
||||
}
|
||||
Console.WriteLine("\nхудший «хороший»: {0:F3} лучший «плохой»: {1:F3} зазор: {2:F3}",
|
||||
gMin, bMax, gMin - bMax);
|
||||
|
||||
// Строка для сборщика — намеренно латиницей: раннер пишет шаги во временный
|
||||
// скрипт в кодировке, которая калечит кириллицу, и проверка по русскому тексту
|
||||
// там развалится.
|
||||
Console.WriteLine("RESULT worst_good={0:F3} best_bad={1:F3}",
|
||||
gMin.ToString("F3", System.Globalization.CultureInfo.InvariantCulture),
|
||||
bMax.ToString("F3", System.Globalization.CultureInfo.InvariantCulture));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user