fix(layers): translate every route proxy refusal and clear stale receiver errorText

The route proxy also refuses with 'routing busy — too many routes at once'
(429) and 'route proxy error'; both showed in English in every locale.
Mark them in ROUTING_REFUSALS and add es, ru, zh-CN and zh-Hant entries.

errorText is now cleared on a new connection, a mode switch and stop(), so
it never outlives the error it describes. Tests cover the two refusals,
'Routing failed: rate limited' (pattern order) and the stop() reset.
This commit is contained in:
Said Turksever committed 2026-10-28 14:13:39 +00:00
1 parent 11aad05dc9
commit 8cedd159e3
9 files changed
+48 -7

No files matched your search

@@ -1,4 +1,5 @@
- Keep Directions route errors, the Local ADS-B receiver error and Global - Keep Directions route errors, the Local ADS-B receiver error and Global
Context cohort reasons English in layer data; the Layers panel, Radio card Context cohort reasons English in layer data; the Layers panel, Radio card
and awareness panel still show them in the interface language and awareness panel still show them in the interface language. The route
(daikaginza, #1209). proxy's "routing busy" and "route proxy error" refusals are now translated
too (daikaginza, #1209).
+3 -1
View File
@@ -3001,5 +3001,7 @@
"Your choice applies now, but could not be saved.": "Tu elección se aplica ahora, pero no se pudo guardar.", "Your choice applies now, but could not be saved.": "Tu elección se aplica ahora, pero no se pudo guardar.",
"Voice replies appear here.": "Las respuestas de voz aparecen aquí.", "Voice replies appear here.": "Las respuestas de voz aparecen aquí.",
"Preview is off.": "La vista previa está desactivada.", "Preview is off.": "La vista previa está desactivada.",
"Automatic stop is unavailable for this voice connection.": "La parada automática no está disponible para esta conexión de voz." "Automatic stop is unavailable for this voice connection.": "La parada automática no está disponible para esta conexión de voz.",
"routing busy — too many routes at once": "servicio de rutas ocupado: demasiadas rutas a la vez",
"route proxy error": "error del proxy de rutas"
} }
+3 -1
View File
@@ -3070,5 +3070,7 @@
"Your choice applies now, but could not be saved.": "Ваш выбор применён, но сохранить его не удалось.", "Your choice applies now, but could not be saved.": "Ваш выбор применён, но сохранить его не удалось.",
"Voice replies appear here.": "Здесь появляются голосовые ответы.", "Voice replies appear here.": "Здесь появляются голосовые ответы.",
"Preview is off.": "Предпросмотр отключён.", "Preview is off.": "Предпросмотр отключён.",
"Automatic stop is unavailable for this voice connection.": "Автоматическая остановка недоступна для этого голосового подключения." "Automatic stop is unavailable for this voice connection.": "Автоматическая остановка недоступна для этого голосового подключения.",
"routing busy — too many routes at once": "сервис маршрутов занят: слишком много маршрутов одновременно",
"route proxy error": "ошибка прокси маршрутов"
} }
+3 -1
View File
@@ -2863,5 +2863,7 @@
"{count} placemarks": { "{count} placemarks": {
"other": "{count} 个地标" "other": "{count} 个地标"
}, },
"S2 Underground publishes these KMZ products to GitHub. New uploads are detected within seconds and replace the previous edition automatically. Click a marker for its report.": "S2 Underground 在 GitHub 发布这些 KMZ 产品。新上传的内容会在数秒内被检测到,并自动替换旧版本。点击标记查看报告。" "S2 Underground publishes these KMZ products to GitHub. New uploads are detected within seconds and replace the previous edition automatically. Click a marker for its report.": "S2 Underground 在 GitHub 发布这些 KMZ 产品。新上传的内容会在数秒内被检测到,并自动替换旧版本。点击标记查看报告。",
"routing busy — too many routes at once": "路线服务繁忙:同时请求的路线过多",
"route proxy error": "路线代理出错"
} }
+3 -1
View File
@@ -2863,5 +2863,7 @@
"{count} placemarks": { "{count} placemarks": {
"other": "{count} 個地標" "other": "{count} 個地標"
}, },
"S2 Underground publishes these KMZ products to GitHub. New uploads are detected within seconds and replace the previous edition automatically. Click a marker for its report.": "S2 Underground 在 GitHub 發布這些 KMZ 產品。新上傳的內容會在數秒內被檢測到,並自動替換舊版本。點擊標記查看報告。" "S2 Underground publishes these KMZ products to GitHub. New uploads are detected within seconds and replace the previous edition automatically. Click a marker for its report.": "S2 Underground 在 GitHub 發布這些 KMZ 產品。新上傳的內容會在數秒內被檢測到,並自動替換舊版本。點擊標記查看報告。",
"routing busy — too many routes at once": "路線服務繁忙:同時請求的路線過多",
"route proxy error": "路線代理出錯"
} }
@@ -99,6 +99,10 @@ for (const locale of ['es', 'ru']) {
const row = rowFor(status, 0); const row = rowFor(status, 0);
assert.ok(row.includes(state.message), row); assert.ok(row.includes(state.message), row);
assert.doesNotMatch(row, /No RTL-SDR device selected/, row); assert.doesNotMatch(row, /No RTL-SDR device selected/, row);
// The English text goes with the error: stop() clears it.
await receiver.stop();
assert.equal(receiver.getState().errorText, null);
} finally { } finally {
setLocale('en'); setLocale('en');
await receiver.destroy(); await receiver.destroy();
+5 -1
View File
@@ -305,6 +305,7 @@ function describeConnectionError(error) {
/** /**
* State fields for a receiver error: `message` in the UI language for the * State fields for a receiver error: `message` in the UI language for the
* Radio card, `errorText` as English data for the Local ADS-B layer's stats. * Radio card, `errorText` as English data for the Local ADS-B layer's stats.
* A new connection, a mode switch and stop() clear `errorText` again.
* @param {string} text English error text (ours or the browser's). * @param {string} text English error text (ours or the browser's).
*/ */
function errorState(text) { function errorState(text) {
@@ -360,6 +361,7 @@ export class SdrController {
gain: this._gainByMode.fm, gain: this._gainByMode.fm,
seeking: false, seeking: false,
seekMessage: '', seekMessage: '',
errorText: null,
snr: null, snr: null,
aircraft: [], aircraft: [],
decodedMessages: 0, decodedMessages: 0,
@@ -1082,6 +1084,7 @@ export class SdrController {
this._setState({ this._setState({
status: 'connecting', status: 'connecting',
message: t('Waiting for an RTL-SDR device…'), message: t('Waiting for an RTL-SDR device…'),
errorText: null,
}); });
let acquisition = null; let acquisition = null;
try { try {
@@ -1554,6 +1557,7 @@ export class SdrController {
message: t('Switching to {mode}…', { message: t('Switching to {mode}…', {
mode: nextMode === 'adsb' ? 'ADS-B' : 'FM', mode: nextMode === 'adsb' ? 'ADS-B' : 'FM',
}), }),
errorText: null,
}); });
try { try {
if (nextMode === 'fm' && !(await this._ensureAudio())) { if (nextMode === 'fm' && !(await this._ensureAudio())) {
@@ -1869,7 +1873,7 @@ export class SdrController {
status: preserveMessage ? this.state.status : idleStatus, status: preserveMessage ? this.state.status : idleStatus,
...(preserveMessage ...(preserveMessage
? {} ? {}
: { message: message || t('RTL-SDR disconnected') }), : { message: message || t('RTL-SDR disconnected'), errorText: null }),
snr: null, snr: null,
samplesPerSecond: 0, samplesPerSecond: 0,
workerBlocks: 0, workerBlocks: 0,
+2
View File
@@ -122,6 +122,8 @@ const ROUTING_REFUSALS = new Set([
msg('routing service is rate limited'), msg('routing service is rate limited'),
msg('route leg too long'), msg('route leg too long'),
msg('route too long'), msg('route too long'),
msg('routing busy — too many routes at once'),
msg('route proxy error'),
]); ]);
const routingRefusal = (text) => const routingRefusal = (text) =>
ROUTING_REFUSALS.has(text) ? t(text) : translateKnown(text); ROUTING_REFUSALS.has(text) ? t(text) : translateKnown(text);
+22
View File
@@ -83,6 +83,28 @@ for (const locale of ['es', 'ru']) {
translateFeedError('Routing failed: something else'), translateFeedError('Routing failed: something else'),
t('Routing failed: {error}', { error: 'something else' }), t('Routing failed: {error}', { error: 'something else' }),
); );
// Every refusal the proxy sends (server/providers/places/routes.js),
// and "rate limited" after "Routing failed:", which the feed pattern
// "<source> rate limited" would otherwise take.
for (const [english, shown] of [
[
'routing busy — too many routes at once — try again in a moment',
t('{error} — try again in a moment', {
error: t('routing busy — too many routes at once'),
}),
],
[
'Routing failed: route proxy error',
t('Routing failed: {error}', { error: t('route proxy error') }),
],
[
'Routing failed: rate limited',
t('Routing failed: {error}', { error: t('rate limited') }),
],
]) {
assert.equal(translateFeedError(english), shown);
assert.doesNotMatch(shown, /routing busy|route proxy|rate limited/);
}
} finally { } finally {
setLocale('en'); setLocale('en');
} }