mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-10-30 23:51:57 +01:00
v2.17.0
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3b34dd9646 |
feat(i18n): translate the DM panel (stage 6)
Covers app/templates/dm.html and app/static/js/dm.js: the sidebar, the searchable conversation selector, the composer, message bubbles and their delivery meta, the Contact Info modal, path management, and the repeater list/map pickers. The DM panel's path management and repeater pickers are the code the My Repeaters panel was adapted from, so its strings reuse the repeaters.* keys added in stage 3a rather than duplicating them. Shared chat chrome (filter bar, FAB titles, composer placeholder, status bar) goes to a new chat.* namespace so index.html and app.js can reuse it in stages 7 and 9, and console.status.* is promoted to common.connected/disconnected/ connecting now that two status bars want the same three words. formatRelativeTimeDm() was the last of the four local relative-time implementations; it is gone, folded into formatTimeAgo() from datetime-utils.js. The contact-info row uses the long form to match the contacts page, the map popup the short form to match My Repeaters. Also fixed along the way, both found by grepping this slice's shared strings against the already-translated panels: - repeaters.js reset the map picker label from JS in three places with a hardcoded English string, so the header translated on load and flipped back to English the moment the picker opened (missed in stage 3a). - tHtml() on a plural key always renders the "one" form, because only tn() passes the count to the resolver. That made the repeater picker's shared-prefix tooltip read "3 repeater ma ten prefiks". Fixed in both callers and now an error in scripts/i18n_check.py, which had no way to catch it. Left English on purpose: Flood/Direct/FLOOD and the SNR labels (glossary), and the 'Device path' label written to the DB on import - it is stored data, so translating it would freeze one language into the row. Catalog: 471 -> 526 keys, pl at 100%. Verified against the local container with i18n-diff.js plus a new i18n-stage6-dm.js that drives the panel through each modal, since the diff tool cannot see hidden ones. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
92117668fb |
feat(i18n): translate the Repeater Management panel (stage 3b)
~110 keys across repeater-manage.html and repeater-manage.js, finishing stage 3.
Catalog is now 243 keys, pl at 100%.
The interesting part is the three declarative schemas — TOOLS, SETTINGS_SECTIONS
and REPEATER_ACTIONS — whose title/label/help/note/desc fields held English
prose. Those fields now hold catalog KEYS, resolved with t() at each render site,
with a comment on each array saying so. The firmware `key` next to them
(radio.rxgain, advert.interval) is untouched: it is a CLI parameter, not text.
That created a checker gap worth naming: a key reached as t(f.label) is invisible
to a scanner looking for t('literal'), so all 57 of them would have been reported
unused. The scanner now also treats a bare string literal that exactly matches a
catalog key as a reference. It only relaxes the unused-key warning — missing-key
errors and the markup lint still come from real call sites, so a typo in a schema
key still surfaces, just at runtime rather than in the checker.
Trimmed the do-not-translate glossary from 19 terms to 10. It fired four
warnings on this slice and three were wrong: "direct neighbours" is an adjective,
not the Direct mode; "telemetria" and "czujniki" are simply the Polish words.
A warning you are supposed to ignore teaches you to ignore all of them, so the
list now holds only jargon whose native translation would stop an operator
matching the UI against firmware output — flood, hop, advert, ACK, RSSI, SNR,
LoRa, MQTT, pubkey, repeater. The rest stayed as guidance in the translator doc.
"flood advert" kept the word in Polish for that reason.
Bulk edits were applied by a script that asserts an exact hit count per
replacement and writes nothing if any count is off — which caught one case where
I had miscounted 7 occurrences as 6.
Verified in the browser: all 57 schema-resolved keys resolve in both languages
(none falls back to its own key), and the page title and toolbar render Polish.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
93c74d150f |
feat(i18n): translate the My Repeaters panel (stage 3a)
~100 keys across repeaters.html and repeaters.js: the list, the add picker, the
password/login flow, the path editor, the map picker, and every toast.
Boundary call made here and written into docs/translations.md: "path" is
translated ("ścieżka"), because it is a word the user reads, not a value the
device reports. The mode names it can hold — Flood, Direct, FLOOD — stay as-is,
as do hop hex, repeater names and login roles. The rule is now stated explicitly
in the translator guide, since "Skonfigurowane path" is exactly the kind of
half-translation that makes a UI worse than leaving it in English.
Removed a third copy of relative-time formatting. Stage 0b merged app.js and
dm.js's formatTime but I grepped for the wrong names and missed
formatRelativeTime; repeaters.js had its own, with a "Never" branch that was
unreachable because its one call site already guards a falsy timestamp. It now
uses the shared formatTimeAgo. contacts.js and dm.js still carry their own
copies — noted in place, folded in with stages 5 and 6.
Two checker fixes, both found by this slice:
- Keys chosen inline, tHtml(x ? 'a.b' : 'c.d'), were invisible to the scanner.
That produced false "unused key" warnings and would have silently hidden a
typo in either branch. Added a second pattern for that shape.
- The generic en/pl diff caught two real misses I had made — the page <title>
and the toast header, both still reading "My Repeaters". Swept every other
template for the same pattern; the remaining ones belong to later stages.
Verified: checker clean, and the diff now reports every comparable string on
/repeaters as changed between en and pl.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
05f502ee53 |
feat(i18n): translate the System Log panel (stage 1)
First vertical slice: /logs chrome in English and Polish. Small on purpose — ~13 strings — but it exercises every part of the mechanism end to end: the head include, the catalog route, the cookie, server-rendered t()/tn(), client-side t()/tHtml()/tn(), a JS-rendered plural, and a rebuilt <select>. Translated: title, pause/clear tooltips, the two "All ..." filter options, the search placeholder, the reset tooltip, the loading and load-failed messages, and the entry counter. Deliberately NOT translated, per the boundary in docs/translations.md: the log lines themselves and the DEBUG/INFO/WARNING/ERROR level names. Those are protocol-side identifiers — the level filter also compares against them, so translating the labels would be a step toward breaking the filter. Marked with a comment in the template so the next person does not "fix" it. The counter is the interesting case: English has two plural forms and Polish four, so "100 entries" is "100 wpisów" while "2 entries" is "2 wpisy". Rendered by tn() through Intl.PluralRules on the client and the matching rule table on the server, which agree. Also fixed in scripts/i18n_check.py: it warned when a markup-free value was used via tHtml(), which is wrong and actively harmful advice. What tHtml() buys at an innerHTML sink is param escaping, not markup support — the warning would have pushed people toward t() in exactly the place where t() is the XSS hazard. The error in the other direction (markup used via t()) stays. Output streams are now forced to UTF-8 so `--missing pl > todo.txt` produces a usable file on Windows rather than mojibake. Verified in the browser: every chrome string differs between en and pl, level names unchanged, no catalog key leaked into the page, the count stays translated after filtering, and the filter bar wraps without overflow at 360px in Polish. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
2ec2f461e2 |
feat(i18n): add UI translation infrastructure (stage 0)
Groundwork for translating the interface. No strings are extracted yet — this stage only adds the mechanism, so the UI is byte-identical in English. Translations are one flat JSON file per language. An admin can add a language by dropping <lang>.json into $MC_CONFIG_DIR/translations/ and picking it in Settings > Appearance — no rebuild, no restart, no compile step. A drop-in file overrides a built-in one of the same name. Built-ins: en (source) and pl. How it works: - app/i18n.py loads and merges catalogs over en.json, so per-key English fallback is baked in server-side and the JS runtime needs no fallback logic. Catalogs are fingerprinted with stat() per render, so a dropped-in file is live on the next refresh rather than the next restart — which matters, because restarting drops the device connection for up to 60s. - Template text renders server-side via t()/t_html()/tn() from inject_globals(), so the first paint is already correct. That context processor covers every render_template() in the app, including the six standalone iframe pages, so routes/views.py needed no changes. - JS text comes from /i18n/<lang>.<hash8>.js, a blocking immutable script shared by all 8 entry points. The hash is in the path, not a query string, so intermediary caches and the service worker bust reliably. - Language = per-browser mc_lang cookie over a server-wide DB default. The cookie is what makes iframes work: they are same-origin, so they send it automatically and the existing modal-open reload wiring needs no changes. t() deliberately does not escape — Jinja autoescape handles that, and escaping here would double-escape every French apostrophe. t_html()/tHtml() escape their params but trust catalog markup. Also fixed along the way: - save_ui_settings() replaced the whole settings blob instead of merging, so a language-only POST would have wiped the toast settings. It worked before only because the one form always submitted every key. - `const t = document.getElementById(...)` in populateUiSettingsForm shadowed the global translation helper; same for `var t` in six theme IIFEs. scripts/i18n_check.py now fails the build if that pattern comes back. - Removed app/templates/contacts.html, dead since the contacts pages were split (no render_template reference anywhere), and two stale CSS comments. Verified locally: catalog route headers/security, drop-in without restart, malformed catalogs logged and skipped rather than offered in the picker, cookie propagation into iframes, and 360px layout. Python and JS agree on interpolation, escaping and Polish plural categories. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |