Main moved the UI to Vue views and Pinia stores, so the Arabic changes to
the old DOM controllers now live in their views: the rail legend range
comes from formatScaleRange (keeping units a source supplies as given),
the voice timeout options are worded by Intl unit formatting in
VoiceControl, and the scene editor's JSON fields stay left to right in
SceneDialog. Main's CCTV status line, launch panel and calibration values
keep the Arabic isolates and Western-digit formatting.
isolate() and keepCodesWithValues() read the locale through getLocale(),
which follows the page's vue-i18n instance, and the Intl formatters take
intlLocale(getLocale()). A language switch now applies in place, and
applyDocumentLanguage sets <html dir> on every switch, so the page mirrors
without a reload.
The icon rails and their tips are placed with logical properties, so they
stay on the outer side of the mirrored stacks; Cyber's new stack offsets,
padding and edge lines are logical too, and rtl.css moves Cyber's right
stack to its new rail clearance. Main removed the Context mini radio, so its
right-to-left rules and test go.
The Arabic catalog gains main's new strings and drops the ones main no
longer uses; the other catalogs keep main's new keys and the branch's
landing labels.
Choosing a language in the picker now applies it at once. The choice is
stored and ?lang is kept in step through replaceState, and the picker keeps
focus.
Code that writes translated text itself re-presents it on a switch:
onLocaleChange() lets an owner listen, removed at teardown, and status
writers hand over a function of the locale (voice cards, scene director,
SDR, draw hints, imagery split, map sources, Bhote Koshi media) so the
words are rebuilt rather than re-sent. The HUD, cockpit, radio, CCTV,
launch, awareness and credit texts follow the same hook, and
applyDocumentTranslations can be applied again without undoing text code
wrote since.
The unsaved-work registry and persistLocaleAndReload existed only to guard
the reload, so they go, with their catalog entries.
The Data, Authoring, Weather, Recent Imagery, Context, CCTV, Street Level
and Radio panels each wrote the same frame by hand. They are now one kit
component, UiPanel, with a title, badge/actions/default slots and
v-model:collapsed, drawing Core's panel markup and classes beside the kit's;
props cover the differences (sideways, glow, scroll, railScroller, tag,
toggleWords). RailPanel binds it to the panel chrome store: v-panel-state
writes its state classes at once, and its button's clicks reach PanelChrome.
UiPanelToggle is the collapse button UiPanel and PanelCollapseButton share.
UiPanel's collapsed drives the button and ui-panel--collapsed
only; the owner hides the body by writing the root's collapsed class, since
the rails measure a panel in the task that opens it. PanelCollapseButton
takes startCollapsed instead of initial and initialTitle, so a button shows
its state's glyph and words before startup presents the panel. Titles render
through t() instead of data-i18n, and the unused "Expand panel" string is
pruned. v-panel-state redraws the presented state after a re-render, in
case a root's class binding ever changes.
A weather source can describe its own products (credit, units, coverage,
legend) through an optional `display` object in each product manifest:
source, credit, units, coverage, info, legend and extent. The weather
layer uses each valid field in the row summary, legend, info text,
coverage navigation, outside-coverage status, layer stats and imagery
credit, and keeps today's built-in value for any field that is absent or
invalid. Units and legend describe one scale, so they apply only as a
valid pair; otherwise both keep their built-in values, and the radar
info heading names the units in use.
Text is single-line and length-capped. It rejects control characters
(U+0000-U+001F, U+007F-U+009F), line and paragraph separators, and
invisible or format characters: soft hyphen (U+00AD), combining grapheme
joiner (U+034F), Arabic letter mark (U+061C), Mongolian vowel separator
(U+180E), zero-width and direction marks (U+200B-U+200F), bidi embedding
and override (U+202A-U+202E), word joiner, invisible operators and bidi
isolates (U+2060-U+2069), variation selectors (U+FE00-U+FE0F), the
byte-order mark (U+FEFF) and tag characters (U+E0000-U+E007F).
Legends need 2-16 stops whose labels are plain decimal numbers or short
text and whose colours are #rrggbb. A number label must read back
exactly as the legend shows it: within the safe integer range, with no
leading zeros and no digits lost to rounding. Extents must be finite, in
range and non-empty. Supplied source and units text is data: the layer
marks it so the panels show it untranslated, while built-in values are
translated as before.
The imagery credit is escaped as plain text and follows a refreshed
manifest even when the shown frame stays the same. A refresh changes
only the credit: the shown frame keeps the snapshot it was staged from,
so a later host switch restages it with its own time and bounds and the
refreshed credit. Manifests without `display`, including the bundled
nowCOAST ones, render exactly as before.
The Cockpit controller no longer writes the HUD's instruments, the flight
plan card, the visor's destination arrow or the vision selector. It keeps
them as plain state, starting from cockpitInstrumentState.js, and
publishes copies to the gev-cockpit store: the instruments on every HUD
update, the route only when it changed, the vision selector when the mode
changes. CockpitView renders the callsign, aircraft meta and emergency
mark, speed and altitude rims and ticks, compass tape, clock, position,
HUD layer, destination arrow and vision selector; a new CockpitRoute
component renders the flight plan card inside the briefing card.
The odometer readouts become CockpitRollingValue components. Their
throttle and digit roll move unchanged from setCockpitRollingValue into
cockpitRollingValue.js: every accepted reading restamps the throttle, a
change inside it is dropped, and each accepted change draws fresh glyphs
so the roll animation restarts. A rim tick with no reading still hides
where it stood, and a hidden arrow keeps its last angle and label.
In a dev build the HUD's Vue patch costs about 0.5 ms per update (1.4 ms
at 4x CPU throttle), against 0.4 ms (2.1 ms) for the DOM writes it
replaces, at about 10 updates a second.
The catalog now passes the gnss-integrity source's windowMs to the layer.
A missing, non-finite or non-positive value keeps the 30-minute default.
The row text now names the window the layer uses instead of a fixed 30 min.
Each side stack hosts a Vue rail listing its panels; selecting one opens it
through PanelChrome. The shell owns one Vue runtime and Pinia instance, and a
panel store mirrors each side's panels from the DOM. The rail hides with its
stack, in Cockpit, clean view and recording, and on phones. Panels without a
catalog entry get a generic item, and Data Layers leaves the rail while the
f shortcut hides it.
The CCTV layer kept every loaded camera live: each view change touched every
icon, every notification rebuilt every camera's state, and the picker listed
the whole catalog. With the default 4,000 cameras, a continental drag ran at
152-179 fps with 18-24 frames over 30 ms (worst 112 ms). It now runs at
235 fps with none. The layer now:
- shows icons only on or near the screen, and when zoomed out keeps one camera
per world-anchored cell (healthy feeds first), so panning keeps the same
cameras;
- grounds and draws coverage only for the cameras in view, the active camera
and its coverage neighbours, retrying a camera at most twice per view regime;
- reruns the view pass while the camera moves, paced by its own cost, and
projects with one matrix per pass instead of a Cesium call per camera;
- keeps the billboard buffer-usage hint fixed, so showing or hiding a few
icons no longer rewrites the vertex data of every billboard;
- lists the cameras in view (active camera first) in the picker, closing with
the count of cameras outside the view, and builds the full camera list in
the UI state only when it is read.
Adds the Authoring labels and tooltips to the de, fr, it, ja, pt and uk
catalogs that arrived from main, and prunes the retired SCENES-era
entries there. The file row's tighter padding and letter spacing keep
one long translated word per line in the narrow Cyber panel
(IMPORTIEREN, ESECUZIONE, ІМПОРТУВАТИ, EJECUCIÓN).
main gained de, fr, it, ja, pt and uk catalogs that still carried the 12
strings this change retires, so their catalog-matches-code tests failed
once the branch met main.
The it, pt and uk catalogs merged from main still carried the two Snow
button strings, which the catalog tests report as stale. They are
pruned with npm run i18n -- --prune.
A Scene Director shot saved with Snow now normalizes to Normal when the
project loads, so the shot list shows NORMAL rather than an untranslated
SNOW for a shot that plays on Normal. The README's style keys read 1-6.
Three-model review (astra, sol, fable), round 3: 17 of 18 findings
applied (some in own wording, one in part); "Missões espac." rejected.
- pt: layer cards «{label}: camada ativada/desativada», pointer and
Directions toasts name «A ferramenta "{owner}"», counters after a
colon where the count can be 1, traffic «sem dados de fluxo» and
«Fluxo: {percent}% cob», «limite de requisições atingido», FECHAR,
ANT., PREDEF. VISUAIS, quoted "Nível da rua", infinitive tooltips,
and more (see docs/translations/pt.md).
- CCTV summary line localizes its CAL, SRC and CONTEXT values through
existing catalog keys (all locales); codes stay underneath.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Three-model review (astra, sol, fable), round 2: 29 of 30 findings
applied, one of them in part.
- pt: FIRMS «mais recente há», CCTV «cruzamentos», degraded radio
directory, «{layers}: ativação recente» for any layer list, «Sem
imagem», NIVELADO, AJUSTA-SE, quoted Provider Settings, V/S kept on
the ADS-B card, no-break space in «(24 h)» / «(30 d)», and more
(see docs/translations/pt.md).
- Malformed cyclone snapshot is now a catalog message (all locales).
- Markup supports data-i18n-context; the radio STATION TAG option uses
"radio tag|All", so it no longer flips gender when the list loads.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Three-model review (astra, sol, fable): 51 of 52 findings applied, two
of them in part.
- pt: AQHI bands as health risk, NAVIO-TANQUE, CANDIDATO DO GOOGLE
PLACES, Provider Settings agreement, retry/feed-down wording,
NOTÍCIAS DA ÁREA (fits the briefing kicker), detection modes in the
masculine, GS and REC kept on the HUD, EXPORTAR PREDEF., prepositions
before times, and more (see docs/translations/pt.md).
- Cyclone request timeout is now a catalog message (all locales).
- Built-in Nepal base shots written by the legacy pack bootstrap now
show their localized titles: displayShotTitle also trusts the legacy
keyframes of an append pack the scene has applied.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The cockpit news kicker no longer cuts off: regional news reads «notícias
locais», the cycle button «CICLO DESLIG.», the subtitle «ÚLTIMAS NOTÍCIAS
DO LOCAL».
- Contact header fits in two lines («AR/MAR 250 KM · INSTALAÇÕES: ATÉ
100 KM»); ground speed reads KT like the rest of the HUD.
- A space between a number and s/h/d («nova tentativa em 30 s», «há 5 h»),
and «aproxime-se de uma rua» for zoom-to-street hints.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Translate the remaining 1,868 strings (pt-BR, as in #909) and review the
contributor entries: HUD codes BRG/SPD/RANGE stay as in the other
languages, sentence-case layer and scene names, Space key named as on the
keyboard, short PREV/NEXT/ON/OFF chips, and the CLDR many form (exact
millions take «de») on every plural.
- Register pt: any pt tag (pt-BR, pt-PT) maps to it; the picker shows
«Português (Brasil)».
- Codes, units and product names identical to English go to
same-as-english.json.
- docs/translations/pt.md: style, plurals, units and glossary.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Brazilian Portuguese locale by abiyeenzo (#909), converted from its
namespaced JS catalogs to the English-keyed pt.json: 859 entries whose
English still matches the current UI.