mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
fix(layers): report Directions errors as English data
The Directions row errors (pointer blocked, no route, routing timed out,
no response from the routing service) and the route request failures
("Routing failed: {error}", "{error} — try again in a moment",
"Routing unavailable (HTTP {status})", the rate-limit and fallback
texts) were translated inside getStats(), so the layer snapshot that
voice tools and the analyst read carried Spanish or Russian text.
Fixed texts are marked with msg() and the interpolated ones are built
in English. The Layers panel translates them where it shows them,
through a ROUTING_ERROR_PATTERNS block in statusText.js that the
proxy's refusal texts move into. English reads as before. In other
locales two things differ slightly: a proxy text inside "Routing
failed: X" that is not one of the known refusals is now looked up in
the catalog instead of shown as given, and the "— try again in a
moment" shape is recognised on every layer's error, not only on
Directions.
This commit is contained in:
1 parent
93ea088f45
commit
4d2a71c1b2
4 files changed
+194
-44
No files matched your search
@@ -38,33 +38,21 @@ export const DIRECTIONS_MODES = Object.freeze({
|
||||
export const DEFAULT_DIRECTIONS_MODE = 'car';
|
||||
|
||||
/**
|
||||
* The route proxy's refusal texts (server/providers/places/routes.js). They
|
||||
* stay English on the wire and in the AI tools; the row shows them translated.
|
||||
* The row error for a failed route request (createDirectionsSource errors),
|
||||
* as English data; the Layers panel shows it translated (ROUTING_ERROR_PATTERNS
|
||||
* in src/ui/statusText.js).
|
||||
*/
|
||||
const ROUTING_REFUSALS = new Set([
|
||||
msg('rate limited'),
|
||||
msg('routing service is rate limited'),
|
||||
msg('route leg too long'),
|
||||
msg('route too long'),
|
||||
]);
|
||||
|
||||
/** A refusal text from the route proxy in the language; unknown ones as given. */
|
||||
export function presentRoutingRefusal(text) {
|
||||
return ROUTING_REFUSALS.has(text) ? t(text) : text;
|
||||
}
|
||||
|
||||
/** The row text for a failed route request (createDirectionsSource errors). */
|
||||
export function presentRoutingFailure(error) {
|
||||
if (error?.status === 429) {
|
||||
return error.refusal
|
||||
? t('{error} — try again in a moment', {
|
||||
error: presentRoutingRefusal(error.refusal),
|
||||
})
|
||||
: t('Routing is rate limited — try again in a moment');
|
||||
? // i18n-ignore: English data, shown through ROUTING_ERROR_PATTERNS
|
||||
`${error.refusal} — try again in a moment`
|
||||
: msg('Routing is rate limited — try again in a moment');
|
||||
}
|
||||
if (Number.isFinite(error?.status) && error.status > 0)
|
||||
return t('Routing unavailable (HTTP {status})', { status: error.status });
|
||||
return error?.message || t('Routing unavailable');
|
||||
// i18n-ignore: English data, shown through ROUTING_ERROR_PATTERNS
|
||||
return `Routing unavailable (HTTP ${error.status})`;
|
||||
return error?.message || msg('Routing unavailable');
|
||||
}
|
||||
|
||||
/** Route colour — the annotation palette's cyan, so voice routes and Directions match. */
|
||||
@@ -643,7 +631,9 @@ export function directionsStats(state) {
|
||||
return {
|
||||
count: route?.steps.length || 0,
|
||||
lastUpdate,
|
||||
error: t('Another map tool is using clicks — close it, then SET A again'),
|
||||
error: msg(
|
||||
'Another map tool is using clicks — close it, then SET A again',
|
||||
),
|
||||
status: 'empty',
|
||||
source,
|
||||
};
|
||||
@@ -662,7 +652,7 @@ export function directionsStats(state) {
|
||||
return {
|
||||
count: 0,
|
||||
lastUpdate,
|
||||
error: error || t('No route found'),
|
||||
error: error || msg('No route found'),
|
||||
status: 'empty',
|
||||
source,
|
||||
};
|
||||
@@ -1144,13 +1134,10 @@ export function createDirectionsLayer({ services, source }) {
|
||||
clearRouteGraphics();
|
||||
_status = 'error';
|
||||
_error =
|
||||
payload?.error === 'no route found'
|
||||
? t('No route found between A and B')
|
||||
: payload?.error
|
||||
? t('Routing failed: {error}', {
|
||||
error: presentRoutingRefusal(payload.error),
|
||||
})
|
||||
: t('No route found between A and B');
|
||||
// i18n-ignore: English data, shown through ROUTING_ERROR_PATTERNS
|
||||
payload?.error && payload.error !== 'no route found'
|
||||
? `Routing failed: ${payload.error}`
|
||||
: msg('No route found between A and B');
|
||||
return;
|
||||
}
|
||||
_route = route;
|
||||
@@ -1167,9 +1154,9 @@ export function createDirectionsLayer({ services, source }) {
|
||||
// happened instead.
|
||||
_error =
|
||||
error?.name === 'AbortError'
|
||||
? t('Routing timed out')
|
||||
? msg('Routing timed out')
|
||||
: error?.name === 'TypeError'
|
||||
? t('Routing unavailable — no response from the routing service')
|
||||
? msg('Routing unavailable — no response from the routing service')
|
||||
: presentRoutingFailure(error);
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
// Directions reports its fixed errors as English data (getStats(), and so the
|
||||
// layer snapshot the voice tools and the analyst read); the Layers panel row
|
||||
// shows them in the UI language.
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { setLocale, t as translate } from '../../i18n/index.js';
|
||||
import { LayerPanel } from '../../ui/layerPanel.js';
|
||||
import { directionsStats, presentRoutingFailure } from './index.js';
|
||||
|
||||
const idle = {
|
||||
enabled: true,
|
||||
mode: 'car',
|
||||
armed: null,
|
||||
a: null,
|
||||
b: null,
|
||||
status: 'idle',
|
||||
error: null,
|
||||
route: null,
|
||||
lastUpdate: null,
|
||||
};
|
||||
|
||||
const cases = [
|
||||
[
|
||||
{ ...idle, pointerBlocked: true },
|
||||
'Another map tool is using clicks — close it, then SET A again',
|
||||
],
|
||||
[{ ...idle, status: 'error' }, 'No route found'],
|
||||
[
|
||||
{ ...idle, status: 'error', error: 'Routing timed out' },
|
||||
'Routing timed out',
|
||||
],
|
||||
];
|
||||
|
||||
function rowFor(stats) {
|
||||
return LayerPanel.prototype._buildMetaText({
|
||||
id: 'directions',
|
||||
enabled: true,
|
||||
stats,
|
||||
});
|
||||
}
|
||||
|
||||
// Route request failures with the proxy's refusal or an HTTP status in them:
|
||||
// [the request error, its English row error, the row text in the language]
|
||||
const failures = () => [
|
||||
[
|
||||
{ status: 429, refusal: 'route leg too long' },
|
||||
'route leg too long — try again in a moment',
|
||||
translate('{error} — try again in a moment', {
|
||||
error: translate('route leg too long'),
|
||||
}),
|
||||
],
|
||||
[
|
||||
{ status: 429, refusal: null },
|
||||
'Routing is rate limited — try again in a moment',
|
||||
translate('Routing is rate limited — try again in a moment'),
|
||||
],
|
||||
[
|
||||
{ status: 503 },
|
||||
'Routing unavailable (HTTP 503)',
|
||||
translate('Routing unavailable (HTTP {status})', { status: 503 }),
|
||||
],
|
||||
[
|
||||
{ status: 0, message: '' },
|
||||
'Routing unavailable',
|
||||
translate('Routing unavailable'),
|
||||
],
|
||||
];
|
||||
|
||||
for (const locale of ['es', 'ru']) {
|
||||
test(`${locale}: Directions errors stay English in stats and read translated in the row`, (t) => {
|
||||
t.after(() => setLocale('en'));
|
||||
setLocale(locale);
|
||||
for (const [state, english] of cases) {
|
||||
const stats = directionsStats(state);
|
||||
assert.equal(stats.error, english);
|
||||
const row = rowFor(stats);
|
||||
assert.ok(row.includes(translate(english)), `${locale}: ${row}`);
|
||||
assert.ok(!row.includes(english), `${locale}: ${row}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (const locale of ['es', 'ru']) {
|
||||
test(`${locale}: route request failures stay English in stats and read translated in the row`, (t) => {
|
||||
t.after(() => setLocale('en'));
|
||||
setLocale(locale);
|
||||
for (const [failure, english, shown] of failures()) {
|
||||
const error = presentRoutingFailure(failure);
|
||||
assert.equal(error, english);
|
||||
const stats = directionsStats({ ...idle, status: 'error', error });
|
||||
assert.equal(stats.error, english);
|
||||
assert.ok(rowFor(stats).includes(shown), `${locale}: ${rowFor(stats)}`);
|
||||
assert.notEqual(shown, english);
|
||||
}
|
||||
// The proxy's refusal after "Routing failed:", and a text it never sends.
|
||||
for (const [refusal, shownRefusal] of [
|
||||
['route too long', translate('route too long')],
|
||||
['something else', 'something else'],
|
||||
]) {
|
||||
const row = rowFor(
|
||||
directionsStats({
|
||||
...idle,
|
||||
status: 'error',
|
||||
error: `Routing failed: ${refusal}`,
|
||||
}),
|
||||
);
|
||||
assert.ok(
|
||||
row.includes(
|
||||
translate('Routing failed: {error}', { error: shownRefusal }),
|
||||
),
|
||||
`${locale}: ${row}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test('en: the Directions row shows the English error', () => {
|
||||
setLocale('en');
|
||||
const row = rowFor(directionsStats({ ...idle, status: 'error' }));
|
||||
assert.match(row, /No route found/);
|
||||
});
|
||||
Reference in new issue
Block a user