mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
fix(voice): show FIRMS and ALPR entity labels translated on the voice card
The pointer chip, its live-region line and the get_entity_context card
title now translate the English entity labels the FIRMS and ALPR layers
register ("Fire · FRP {frp} MW", "ALPR camera"). Other names, such as
callsigns and place names, show as they came.
This commit is contained in:
1 parent
93ea088f45
commit
681ec84748
3 files changed
+77
-6
No files matched your search
+13
-1
@@ -115,6 +115,18 @@ function shownSources(source) {
|
||||
return source ? [{ label: sourceLabel(source) }] : [];
|
||||
}
|
||||
|
||||
// Entity labels that layers register as English data (firms/, alpr/); keep in
|
||||
// step with the producers. Other names (callsigns, places) show as they came.
|
||||
const ENTITY_LABELS = new Set([msg('ALPR camera')]);
|
||||
const FIRE_LABEL = /^Fire · FRP (\S+) MW$/;
|
||||
|
||||
/** An entity label as the card shows it, in the active language. */
|
||||
export function shownEntityLabel(label) {
|
||||
if (ENTITY_LABELS.has(label)) return t(label);
|
||||
const fire = FIRE_LABEL.exec(label ?? '');
|
||||
return fire ? t('Fire · FRP {frp} MW', { frp: fire[1] }) : label;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tool errors stay English in the result (the model reads them). The card
|
||||
* translates the known ones; the English must match gevActions exactly.
|
||||
@@ -592,7 +604,7 @@ function getEntityContext(result) {
|
||||
return {
|
||||
say: null,
|
||||
display: {
|
||||
title,
|
||||
title: spokenLabel(shownEntityLabel(title)),
|
||||
lines: [
|
||||
selected.layerName
|
||||
? shownLayerName(selected.layerName, selected.layerId)
|
||||
|
||||
@@ -3,8 +3,16 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { setLocale } from '../i18n/index.js';
|
||||
import { attachVoiceResult, planStepLabel } from './speech.js';
|
||||
import { voiceCardView } from './voiceCardPresentation.js';
|
||||
import {
|
||||
attachVoiceResult,
|
||||
planStepLabel,
|
||||
shownEntityLabel,
|
||||
} from './speech.js';
|
||||
import {
|
||||
initialVoiceCardState,
|
||||
reduceVoiceCard,
|
||||
voiceCardView,
|
||||
} from './voiceCardPresentation.js';
|
||||
|
||||
for (const locale of ['ru', 'es']) {
|
||||
test(`voice card is in ${locale} for a layer switch`, (t) => {
|
||||
@@ -86,3 +94,49 @@ test('en: layer switch, distance and plan labels in English', () => {
|
||||
'Earthquakes (24h) on',
|
||||
);
|
||||
});
|
||||
|
||||
// FIRMS and ALPR register English entity labels: the model reads them as data
|
||||
// (get_entity_context, pointer_context), the card shows them translated.
|
||||
const ENTITY_LABELS = {
|
||||
ru: { fire: 'Пожар · FRP 12 МВт', alpr: 'Камера ALPR' },
|
||||
es: { fire: 'Incendio · FRP 12 MW', alpr: 'Cámara ALPR' },
|
||||
};
|
||||
|
||||
for (const [locale, shown] of Object.entries(ENTITY_LABELS)) {
|
||||
test(`entity labels stay English in the result and show in ${locale}`, (t) => {
|
||||
t.after(() => setLocale('en'));
|
||||
setLocale(locale);
|
||||
const out = attachVoiceResult('get_entity_context', {
|
||||
ok: true,
|
||||
selected: {
|
||||
id: 'fire-1',
|
||||
name: 'Fire · FRP 12 MW',
|
||||
layerId: 'local-firms',
|
||||
layerName: 'NASA FIRMS',
|
||||
},
|
||||
});
|
||||
assert.equal(out.selected.name, 'Fire · FRP 12 MW');
|
||||
assert.equal(out.display.title, shown.fire);
|
||||
|
||||
const state = reduceVoiceCard(initialVoiceCardState(), {
|
||||
type: 'pointer',
|
||||
pointer: { kind: 'this', label: 'ALPR camera' },
|
||||
});
|
||||
assert.equal(state.pointer.label, 'ALPR camera');
|
||||
assert.equal(voiceCardView(state).pointer.text, shown.alpr);
|
||||
assert.equal(state.announce, `This: ${shown.alpr}`);
|
||||
});
|
||||
}
|
||||
|
||||
test('only the registered entity labels are translated', () => {
|
||||
assert.equal(shownEntityLabel('Fire · FRP 4.9 MW'), 'Fire · FRP 4.9 MW');
|
||||
assert.equal(shownEntityLabel('ALPR camera'), 'ALPR camera');
|
||||
setLocale('ru');
|
||||
try {
|
||||
assert.equal(shownEntityLabel('Fire · FRP 4.9 MW'), 'Пожар · FRP 4.9 МВт');
|
||||
assert.equal(shownEntityLabel('UPS793'), 'UPS793');
|
||||
assert.equal(shownEntityLabel('Fire Station 5'), 'Fire Station 5');
|
||||
} finally {
|
||||
setLocale('en');
|
||||
}
|
||||
});
|
||||
@@ -1,4 +1,9 @@
|
||||
import { planStepLabel, progressStepLabel, spokenLabel } from './speech.js';
|
||||
import {
|
||||
planStepLabel,
|
||||
progressStepLabel,
|
||||
shownEntityLabel,
|
||||
spokenLabel,
|
||||
} from './speech.js';
|
||||
import { presentResult } from './resultDisplay.js';
|
||||
import { MAX_DISPLAYED_REFERENTS, normalizeReferents } from './referents.js';
|
||||
|
||||
@@ -132,7 +137,7 @@ export function reduceVoiceCard(state, event) {
|
||||
return reveal({
|
||||
...state,
|
||||
pointer,
|
||||
announce: `${kind}: ${spokenLabel(pointer.label, 32)}`,
|
||||
announce: `${kind}: ${spokenLabel(shownEntityLabel(pointer.label), 32)}`,
|
||||
});
|
||||
}
|
||||
case 'transcript': {
|
||||
@@ -288,7 +293,7 @@ export function voiceCardView(state) {
|
||||
pointer: {
|
||||
visible: Boolean(state.pointer),
|
||||
kind: state.pointer?.kind === 'here' ? t('HERE') : t('THIS'),
|
||||
text: spokenLabel(state.pointer?.label || '', 32),
|
||||
text: spokenLabel(shownEntityLabel(state.pointer?.label || ''), 32),
|
||||
},
|
||||
assistant: {
|
||||
visible: Boolean(state.assistant),
|
||||
|
||||
Reference in new issue
Block a user