fix(hud): ask again in the new language after an in-place switch

- _relocalize() now drops the received reply and aborts one in flight, so
  the next tick asks in the new language instead of keeping old-language
  text under the new-language state tag.
- The English-echo check also allows state words inside a place label
  (Estación Loading Dock).
- Reword the non-English instruction so it reads for every language name.
- Add the two new test files to the format scope.
This commit is contained in:
German Khokhlov committed 2026-11-02 12:00:44 +04:00
1 parent 1a6687632d
commit 0d3c1ef1d9
5 files changed
+64 -8

No files matched your search

+12 -5
View File
@@ -132,7 +132,8 @@ function withProvenance(text, provenance) {
/**
* The English a summary outside English must not carry: feed-state ids and
* translated layers' English names. A state word the locale keeps in English
* (de FALLBACK) and a layer name inside a place label (Radio City) are fine.
* (de FALLBACK) and words inside a place label (Radio City, Loading Dock) are
* fine.
*/
function summaryEnglishWords(context) {
const places = [
@@ -144,7 +145,9 @@ function summaryEnglishWords(context) {
.toLowerCase();
return [
...Object.keys(FEED_STATE_LABELS).filter(
(state) => t(FEED_STATE_LABELS[state]).toLowerCase() !== state,
(state) =>
t(FEED_STATE_LABELS[state]).toLowerCase() !== state &&
!places.includes(state),
),
...(context.translatedLayerNames || []).filter(
(name) => !places.includes(name.toLowerCase()),
@@ -323,11 +326,15 @@ export class IntelHUD {
}
/**
* Show the HUD's own words in the new locale. The deterministic summary
* is composed again; the AI reply does not depend on the locale, so the
* next tick shows the one already received rather than asking again.
* Show the HUD's own words in the new locale. The AI reply is written in
* the locale too, so the one received is dropped like a camera move drops
* it: a request in flight is aborted and the next tick asks again.
*/
_relocalize() {
this._markSummaryDirty();
this._lastSummarySignature = null;
this._lastSummaryText = null;
this._summaryRequest?.abort();
this._present({
mode: MODE_LABELS[this._currentStyle]
? t(MODE_LABELS[this._currentStyle])
+47
View File
@@ -185,6 +185,7 @@ for (const [locale, summary, places] of [
['es', 'Tráfico aéreo nominal sobre Madrid', ['Madrid']],
['de', 'Fallback Daten über Berlin', ['Berlin']],
['ja', 'Live Flights Plaza周辺', ['Live Flights Plaza']],
['es', 'Estación Loading Dock Madrid', ['Estación Loading Dock']],
]) {
test(`a ${locale} word that is not an echo is shown: ${summary}`, async (t) => {
const { hud, shown } = createHud(
@@ -196,3 +197,49 @@ for (const [locale, summary, places] of [
assert.ok(shown().includes(summary), shown());
});
}
test('a language switch drops the reply in flight and asks again', async (t) => {
const { hud, sent, shown } = createHud(t, {});
let answer;
hud.summaryService.summarize = (context, { signal }) => {
sent.push(context);
return new Promise((resolve, reject) => {
answer = () =>
resolve({
ok: true,
status: 200,
data: {
error: null,
summary: 'Остин рейсы частично',
feedState: 'partial',
},
});
signal.addEventListener('abort', () =>
reject(Object.assign(new Error('aborted'), { name: 'AbortError' })),
);
});
};
const pending = hud._updateSummary(false, true);
while (!answer) await new Promise((resolve) => setImmediate(resolve));
setLocale('es');
answer();
await pending;
assert.ok(!shown().includes('Остин'), shown());
assert.equal(shown(), hud._composeSummary());
hud.summaryService.summarize = async (context) => {
sent.push(context);
return {
ok: true,
status: 200,
data: {
error: null,
summary: 'Austin vuelos parciales',
feedState: 'partial',
},
};
};
await hud._updateSummary(false);
assert.equal(sent.at(-1).locale, 'es');
assert.ok(shown().startsWith('PARCIAL | Austin vuelos parciales'), shown());
});
+1 -1
View File
@@ -165,7 +165,7 @@ export function hudSummaryInstructions(locale) {
const length = hudSummaryIsUnspaced(tag)
? ` Instead of five words, write one phrase of at most ${HUD_SUMMARY_MAX_UNSPACED_CHARS} characters.`
: '';
return `${HUD_SUMMARY_INSTRUCTIONS} Write the summary in ${language} (${tag}), not English, giving places their usual ${language} names and layers the names in enabledLayerLabels; feedState stays the English value.${length}`;
return `${HUD_SUMMARY_INSTRUCTIONS} Write the summary in ${language} (${tag}), not English. Give places their usual names in that language and layers the names in enabledLayerLabels; feedState stays the English value.${length}`;
}
/**