i18n: add the 48 UI strings no language file had, as empty rows for translators

The firmware asked for these keys and no .lang file carried them, so they
rendered English in all 13 languages with nothing to show a translator that
they were missing. Mostly strings that shipped with a feature whose PR did not
touch the language files: the SD arbitration and migration warnings, the
Wi-Fi/BLE coexistence messages, the keyboard-navigation vocabulary
(Up/Down/Left/Right/Select/Scroll) and section names (General, Clock & time,
Sensors, Screen, Home, Messages, Keys, Compact, Snake).

They go in with an EMPTY translation, which is the format's own way of saying
'not translated yet': the runtime loader requires a non-empty value
(`if (*p && tab[1])`) and gen-lang-builtin.py requires `if k and v`, so both
skip these rows and the English falls through exactly as before. Confirmed:
i18n_builtin.h regenerates byte-identical. No `# ver:` bump for the same
reason — device behaviour is unchanged, so there is nothing to re-download.

Also fixes the audit's own row parser to match the loader: a line is a comment
only when it has NO tab. Testing for a leading '#' hid every key starting with
LVGL recolor markup ("#7A7F87 Wardrive: …#"), the exact class the loader has a
comment warning about.

Every language is now at 0 missing keys.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kaj Schittecat
2026-08-10 10:24:56 +02:00
co-authored by Claude Opus 5
parent 69bace7c15
commit d4ade2e616
14 changed files with 632 additions and 2 deletions
+8 -2
View File
@@ -83,11 +83,17 @@ def audit(path, keys):
rows = []
for line in open(path, encoding='utf-8'):
line = line.rstrip('\n')
if not line or line.startswith('#') or '\t' not in line: continue
# A line is a header/comment only when it has NO tab — same rule as the
# loader. Testing for a leading '#' would drop every key that starts with
# LVGL recolor markup ("#7A7F87 Wardrive: …#"), which are real keys.
if '\t' not in line: continue
en, tr = line.split('\t', 1)
rows.append((c_unescape(en), tr))
dead = [en for en, tr in rows if en not in keys]
missing = sorted(k for k in keys if k and k not in {en for en, _ in rows})
# Keys with no letters (a lone emoji, a glyph-only button) translate to
# themselves — not worth a row, and not a gap.
missing = sorted(k for k in keys
if k and k not in {en for en, _ in rows} and re.search(r'[A-Za-z]', k))
name = os.path.basename(path)
print(f'\n=== {name}{len(rows)} rows vs {len(keys)} TR() keys ===')
print(f' translated but NEVER looked up : {len(dead)}')