Files
gods-eye-view/src/creditKeyboard.js
T
German KhokhlovandClaude Opus 5.5 00704ddc89 i18n: fix review findings and polish ru/es strings
Fixes the 51 P0–P2 findings from the full review of the i18n branch and
the issues found while checking every new string on screen in ru and es.

- Translate errors and statuses that still reached ru/es in English:
  feed and tile errors, provider statuses, the ALPR chip, the traffic
  mode chip, voice plan step labels, fire feed values, receiver bands,
  scene save reasons and the Cesium attribution link.
- Show preset city names in the UI language (src/ui/placeNames.js);
  data and everything sent to the model stay English.
- Share layer names between the layer panel, HUD and voice card
  (src/ui/layerNames.js).
- Keep long ru/es strings whole: voice card error lines, analyst
  titles, the voice tier status, the Nepal evidence button, the ALPR
  retry chip. English layout is unchanged.
- Ask before a language switch drops unsaved work.
- Catalog fixes: glossary terms (пуск, пресет), gender agreement and
  consistent "peticiones" in es, fewer calques in both languages.
- Fix English text that was itself wrong (e.g. "Exit cockpit to fly to
  a weather", raw tool ids in voice plan steps).

Catalogs: es/ru 2535 messages each, complete.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 23:49:52 +04:00

85 lines
3.0 KiB
JavaScript

/**
* Adds the keyboard semantics omitted by Cesium's href-less attribution links.
* Cesium retains ownership of showing and hiding its required credit lightbox.
*/
import { getLocale, t } from './i18n/index.js';
export function configureCreditKeyboardAccess(root = document) {
const expand = root?.querySelector?.(
'#cesium-credits .cesium-credit-expand-link',
);
const lightbox = root?.querySelector?.('.cesium-credit-lightbox');
const close = lightbox?.querySelector?.('.cesium-credit-lightbox-close');
if (!expand || !lightbox || !close) return false;
const overlay = lightbox.parentElement;
// Cesium writes these two texts in English once, when it builds the credit
// display, and never rewrites them; English keeps Cesium's own text.
if (getLocale() !== 'en') {
expand.textContent = t('Data attribution');
const title = lightbox.querySelector?.('.cesium-credit-lightbox-title');
if (title) title.textContent = t('Data provided by:');
}
if (!lightbox.id) lightbox.id = 'cesium-credit-lightbox';
expand.setAttribute('role', 'button');
expand.setAttribute('tabindex', '0');
expand.setAttribute('aria-haspopup', 'dialog');
expand.setAttribute('aria-controls', lightbox.id);
expand.setAttribute('aria-expanded', 'false');
close.setAttribute('role', 'button');
close.setAttribute('tabindex', '0');
close.setAttribute('aria-label', t('Close data attribution'));
if (expand.dataset.gevKeyboardReady === 'true') return true;
expand.dataset.gevKeyboardReady = 'true';
const installKeyboardActivation = (control) => {
let spacePressed = false;
control.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
event.preventDefault();
if (!event.repeat) control.click();
return;
}
if (event.key !== ' ') return;
event.preventDefault();
if (!event.repeat) spacePressed = true;
});
control.addEventListener('keyup', (event) => {
if (event.key !== ' ') return;
event.preventDefault();
if (!spacePressed) return;
spacePressed = false;
control.click();
});
// A long Space hold may hand control to push-to-talk and move focus away.
// Cancelling the armed release prevents that eventual keyup from also
// activating the attribution control.
control.addEventListener('blur', () => {
spacePressed = false;
});
};
const closeAndRestoreFocus = () => {
expand.setAttribute('aria-expanded', 'false');
expand.focus();
};
installKeyboardActivation(expand);
installKeyboardActivation(close);
lightbox.addEventListener('keydown', (event) => {
if (event.key !== 'Escape' || event.defaultPrevented) return;
event.preventDefault();
event.stopPropagation();
close.click();
});
expand.addEventListener('click', () => {
expand.setAttribute('aria-expanded', 'true');
close.focus();
});
close.addEventListener('click', closeAndRestoreFocus);
overlay?.addEventListener?.('click', (event) => {
if (!lightbox.contains(event.target)) closeAndRestoreFocus();
});
return true;
}