Files
Kaj SchittecatandClaude Opus 5 a9cff6f113 i18n: translate the map credits sheet, and make the baked table follow the files
The map About/credits sheet was 820 bytes of raw English built with snprintf and
no TR() anywhere in it, so it stayed English in every language (#257). It is now
three keys: the two attribution headers (the OpenTopoMap variant is credited
separately because its style is CC-BY-SA) and the body, kept whole rather than
split per paragraph so translators get prose instead of fragments. The buffer
grows 820 -> 2048 because Hungarian runs about 1.5x English here and the
Cyrillic and Greek files are two bytes a letter.

Hungarian text from pisti87 (#257). Two edits to what he posted, both flagged on
the issue: the hard line breaks he inserted at the English wrap points are gone,
because the label wraps itself and a fixed break lands mid-sentence on any other
panel width; and the header reads "Terkep adatok" rather than "Map adatok",
which looked like a copy-paste artifact given the rest is fully translated. The
OpenTopoMap variant is derived from his own wording and is his to correct.

Also raw, from the same report: the Discovered auto-add hint and the four type
words it interpolates. The hint buffer goes to 240 bytes and the type list to
128, since the translated plurals are longer than "chats, repeaters".

The reason none of that would have shipped: gen-lang-builtin.py exists so the
baked-in table and the .lang files the store serves cannot drift, and its
docstring promises a pre-build step that runs it. Nothing ran it. Editing a
.lang and building produced an image carrying the OLD translations, silently.
It is now a real pre: hook on all seven PlatformIO envs and a line in both IDF
build scripts, regenerating only when a .lang is newer than the header.

deploy-apps.sh grew the matching check for the other half of that path: a
catalog version that disagrees with the file's own "# ver:" publishes
translations to a version no device asks for.

All thirteen languages snapshot to v17 -- the merged region and SD work added
keys to every file, not just Hungarian.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:06:39 +02:00

87 lines
3.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# wadamesh app+language store deploy: publish deploy/apps/ to the firmware VPS.
#
# The device reads BOTH catalogs from the firmware host, NOT the site host:
# http://firmware.wadamesh.com/apps/apps.json <- Store > Apps
# http://firmware.wadamesh.com/apps/langs.json <- Store > Languages
# http://firmware.wadamesh.com/apps/lang/<ver>/<code>.lang
# so this tree lives under the FIRMWARE web root, alongside releases/ and latest*/.
#
# WHY THIS SCRIPT EXISTS: there wasn't one. release.sh rsyncs out/firmware/ (which
# does not contain apps/) and deploy-site.sh rsyncs deploy/site/ — so the store was
# only ever updated by someone remembering to rsync it by hand, and twice they did
# not. Translations sat at v8 on the server while the repo had v9/v10/v11, and a
# published app never appeared at all. Editing deploy/apps/ is not publishing it;
# running this is.
#
# Usage:
# WADAMESH_VPS=user@your-vps scripts/deploy-apps.sh # deploy
# WADAMESH_VPS=user@your-vps scripts/deploy-apps.sh --dry-run # preview only
#
# Optional:
# WADAMESH_FW_PATH=/srv/wadamesh/firmware # override the firmware web root
#
# The VPS target comes from the environment — never commit it.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
SRC="$ROOT/deploy/apps/"
DEST="${WADAMESH_VPS:?set WADAMESH_VPS=user@host}"
DEST_PATH="${WADAMESH_FW_PATH:-/srv/wadamesh/firmware}/apps"
DRY=""
[ "${1:-}" = "--dry-run" ] && DRY="--dry-run"
# Sanity-check the catalogs before shipping them: a device that gets malformed JSON
# shows an empty store with no clue why, and the versioned .lang dir each catalog
# entry points at has to actually exist or the download 404s on the device.
python3 - "$ROOT" <<'PY'
import json, os, sys
root = sys.argv[1]
apps_dir = os.path.join(root, "deploy", "apps")
langs = json.load(open(os.path.join(apps_dir, "langs.json")))["langs"]
missing = [f"{l['code']} v{l['ver']}" for l in langs
if not os.path.isfile(os.path.join(apps_dir, "lang", str(l["ver"]), l["code"] + ".lang"))]
if missing:
sys.exit("ABORT: langs.json points at files that do not exist: " + ", ".join(missing))
# The catalog version is what makes a device re-download a language. If it does
# not match the "# ver:" line inside the file, translators' work is published to
# a version nobody fetches and the change is invisible on device.
import re
drift = []
for l in langs:
f = os.path.join(apps_dir, "lang", str(l["ver"]), l["code"] + ".lang")
m = re.search(r"^# ver:\s*(\S+)", open(f, encoding="utf-8").read(), re.M)
if m and m.group(1) != str(l["ver"]):
drift.append(f"{l['code']}: catalog v{l['ver']} but file says v{m.group(1)}")
if drift:
sys.exit("ABORT: langs.json and the .lang headers disagree: " + "; ".join(drift))
# Same check for apps: an entry whose <id>/<ver>/<id>.lua is absent shows up in the
# Store and then fails to install, with nothing on screen to say why.
apps = json.load(open(os.path.join(apps_dir, "apps.json")))["apps"]
bad = []
for a in apps:
base = os.path.join(apps_dir, a["id"], a["ver"])
for f in (a["id"] + ".lua", a["id"] + ".json"):
if not os.path.isfile(os.path.join(base, f)):
bad.append(f'{a["id"]} v{a["ver"]}: {f}')
if bad:
sys.exit("ABORT: apps.json points at files that do not exist: " + ", ".join(bad))
print(f"catalogs ok: {len(langs)} languages and {len(apps)} apps, every referenced file present")
PY
# --delete mirrors the repo: the store is generated content, the repo is the source
# of truth, and stale versioned dirs left on the server are how a device ends up
# offered a language file nothing points at any more.
rsync -avz $DRY --delete "$SRC" "$DEST:$DEST_PATH/"
if [ -z "$DRY" ]; then
echo
echo "deployed deploy/apps/ -> $DEST:$DEST_PATH"
echo "verify:"
echo " curl -s http://firmware.wadamesh.com/apps/langs.json"
echo " curl -s http://firmware.wadamesh.com/apps/apps.json"
fi