From e11debbda44905bc451fe943cc5f320469b9a8a7 Mon Sep 17 00:00:00 2001 From: MarekWo Date: Sat, 1 Aug 2026 09:39:52 +0200 Subject: [PATCH] feat(i18n): translate the app chrome and main chat window (stage 7a) Covers the navbar, the offcanvas main menu, and index.html: the channel sidebar, filter bar, composer, status bar and the FAB column, plus the six fullscreen panel modals, whose headers now reuse each panel's own title key (dm.title, contacts.title, console.title, repeaters.title, pa.title, logs.title) instead of repeating the words. The Settings modal (base.html ~458-1147) and the other modals in base.html are untouched - stages 8 and 7b. Also translated in app.js: updateStatus(), updateLastRefresh(), the FAB collapse toggle, the filter bar's "no matches" panel and updateRegionIndicator(). The rest of app.js stays for stage 9; the line drawn here is "does it overwrite a string index.html just got a key for". Without them the status bar renders Polish server-side and flips back to English a second later, which would make the slice impossible to verify - a miss and a stage-9 leftover look identical. "Filter messages" (menu) and "Filter Messages" (FAB) keep separate keys even though the Polish is identical, so this commit changes no English text, only where it lives. Catalog: 526 -> 573 keys, pl at 100%. Verified with i18n-diff.js and a new i18n-stage7a-chrome.js that opens the offcanvas menu at both viewports - like the DM modals, a closed offcanvas is invisible to the plain diff. Two tooling fixes came out of it, both in F:\tmp\pwverify: - i18n-diff.js checked visibility on the text node's parent only, so every closed modal's contents landed in the report: 275 flagged lines on the main chat, 13 after walking the ancestor chain. It was noise, never a false pass, but it is the kind of noise that trains you to skim the list. - browser.js now centralises the Chrome launch. The translate bubble was held off by one flag copy-pasted into each script; it is now flags plus a persistent profile with translation disabled in Preferences, which is the part that survives Chrome renaming the feature. Co-Authored-By: Claude Opus 5 --- app/static/js/app.js | 20 ++++----- app/templates/base.html | 96 ++++++++++++++++++++-------------------- app/templates/index.html | 80 ++++++++++++++++----------------- app/translations/en.json | 47 ++++++++++++++++++++ app/translations/pl.json | 47 ++++++++++++++++++++ 5 files changed, 192 insertions(+), 98 deletions(-) diff --git a/app/static/js/app.js b/app/static/js/app.js index f1e48d3..9ba540d 100644 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -4146,12 +4146,12 @@ function updateRegionIndicator() { nameEl.textContent = scope.name; el.classList.remove('bg-light', 'text-secondary', 'border'); el.classList.add('bg-info', 'text-dark'); - el.title = 'Click to change region for this channel'; + el.title = t('chat.region_title'); } else { - nameEl.textContent = 'No region'; + nameEl.textContent = t('chat.region_none'); el.classList.remove('bg-info', 'text-dark'); el.classList.add('bg-light', 'text-secondary', 'border'); - el.title = 'Click to set a region for this channel'; + el.title = t('chat.region_set_title'); } } @@ -4334,9 +4334,9 @@ function updateStatus(status) { const statusEl = document.getElementById('statusText'); const icons = { - connected: ' Connected', - disconnected: ' Disconnected', - connecting: ' Connecting...' + connected: ` ${tHtml('common.connected')}`, + disconnected: ` ${tHtml('common.disconnected')}`, + connecting: ` ${tHtml('common.connecting')}` }; statusEl.innerHTML = icons[status] || icons.connecting; @@ -4348,7 +4348,7 @@ function updateStatus(status) { function updateLastRefresh() { const now = new Date(); const timeStr = now.toLocaleTimeString(); - document.getElementById('lastRefresh').textContent = `Updated: ${timeStr}`; + document.getElementById('lastRefresh').textContent = t('chat.updated', { time: timeStr }); } /** @@ -6339,13 +6339,13 @@ function initializeFabToggle() { // Restore collapsed state if (localStorage.getItem('mc-webui-fab-collapsed') === '1') { container.classList.add('collapsed'); - toggle.title = 'Show buttons'; + toggle.title = t('chat.fab.show'); } toggle.addEventListener('click', () => { container.classList.toggle('collapsed'); const isCollapsed = container.classList.contains('collapsed'); - toggle.title = isCollapsed ? 'Show buttons' : 'Hide buttons'; + toggle.title = isCollapsed ? t('chat.fab.show') : t('chat.fab.hide'); localStorage.setItem('mc-webui-fab-collapsed', isCollapsed ? '1' : '0'); }); @@ -6620,7 +6620,7 @@ function applyFilter(query) { noMatchesDiv.className = 'filter-no-matches'; noMatchesDiv.innerHTML = ` -

No messages match "${escapeHtml(currentFilterQuery)}"

+

${tHtml('chat.filter.no_matches', { query: currentFilterQuery })}

`; container.appendChild(noMatchesDiv); } diff --git a/app/templates/base.html b/app/templates/base.html index 4cc8c7a..d238fdb 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -63,13 +63,13 @@ - {{ device_name }} {% endif %} {% if transport_type == 'ble' %} - BLE + BLE {% elif transport_type == 'tcp' %} - TCP + TCP {% endif %}
- @@ -92,7 +92,7 @@ diff --git a/app/templates/index.html b/app/templates/index.html index 03aa6a4..f4b45c4 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Chat - mc-webui{% endblock %} +{% block title %}{{ t('chat.title') }} - mc-webui{% endblock %} {% block extra_head %} @@ -14,7 +14,7 @@
- Channels + {{ t('chat.channels') }}
@@ -28,20 +28,20 @@
- +
- - -
@@ -51,14 +51,14 @@
- Loading... + {{ t('common.loading') }}
-

Loading messages...

+

{{ t('chat.loading_messages') }}

-
@@ -70,12 +70,12 @@ -
@@ -115,46 +115,46 @@
- - - - - - - - - - - - - -
@@ -164,9 +164,9 @@