fix(i18n): second screenshot review: remaining English, numbers and wraps

From the re-shot Russian screenshots:
- Translated: weather frame/imagery status errors, HUD summary provenance
  ("НЕДОСТУПНО: СУДА AIS ОНЛАЙН"), Bhote Koshi map labels, cockpit signal
  distances (КМ). The HUD summary fields are separated with " · " in every
  language, so the style name and view band no longer read as one phrase.
- Numbers follow the locale: cockpit altitude ("26 175"), precipitation.
- No breaks inside codes and dates: ADS-B and similar in layer rows,
  Sentinel-2, cyclone issue dates (word joiner; the UI font has no U+2011),
  no-break space after label colons in cyclone details.
- Layout: count lines under ALPR, short unavailable chip (context
  "status chip"), roomier draw segments, tighter CCTV calibration chips,
  two-line beat titles and media card titles, wrapping weather status.
- Wording: ru advisory is «сводка» (was «бюллетень»; "Advisory center" is
  «Центр циклона по сводке»), Street View is «Просмотр улиц», OPACITY
  «НЕПРОЗР.», synced «синхр.», Area «Зона»; the import dialog no longer says
  "this scene" for several scenes. Glossary updated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-13 19:13:12 +04:00
1 parent 2b6ccad732
commit 54dce5b3bd
32 files changed
+1204 -346

No files matched your search

+5 -1
View File
@@ -1,3 +1,5 @@
import { msg } from '../../i18n/index.js';
/** Resolve the visible surface that can own weather imagery without scene mutation. */
export function resolveImageryHost({ viewer, tileset }) {
if (viewer?.scene?.globe?.show === true)
@@ -7,7 +9,9 @@ export function resolveImageryHost({ viewer, tileset }) {
return { collection: null, kind: 'none' };
}
export const NO_IMAGERY_HOST = 'Hidden by this map source · choose a globe map';
export const NO_IMAGERY_HOST = msg(
'Hidden by this map source · choose a globe map',
);
/** Report imagery suspension without changing or detaching the resolved host. */
export function imageryHostStatus(host) {
+17 -10
View File
@@ -7,7 +7,7 @@ import {
GLOBAL_INFRARED_MAX_GAP_MS,
LIGHTNING_MAX_GAP_MS,
} from './clock.js';
import { t } from '../../i18n/index.js';
import { t, msg } from '../../i18n/index.js';
import { formatScaleLabel } from '../../ui/unitLabels.js';
const STOPS = [
@@ -229,12 +229,14 @@ export function createWeatherLayer({
rendering.setHidden?.(false);
void warmNext(time);
}
error = ok ? null : 'Frame unavailable; previous observation retained';
error = ok
? null
: msg('Frame unavailable; previous observation retained');
if (!ok) stop();
return ok;
} catch {
if (owner === generation && !controller.signal.aborted) {
error = 'Weather imagery unavailable';
error = msg('Weather imagery unavailable');
stop();
}
return false;
@@ -340,7 +342,9 @@ export function createWeatherLayer({
if (!enabled || controller.signal.aborted || request !== controller)
return false;
if (snapshot.unavailable) {
error = 'Weather source unavailable; previous observation retained';
error = msg(
'Weather source unavailable; previous observation retained',
);
stop();
return true;
}
@@ -370,7 +374,7 @@ export function createWeatherLayer({
return !controller.signal.aborted && request === controller && enabled;
} catch (cause) {
if (controller.signal.aborted || request !== controller) return false;
error = cause?.message || 'Weather unavailable';
error = cause?.message || msg('Weather unavailable');
stop();
return true;
} finally {
@@ -524,6 +528,10 @@ export function createWeatherLayer({
})
: t('{minutes}m ago', { minutes: age });
const diagnostic = rendering?.getDiagnostics();
// Errors are stored in English (stats, diagnostics); shown translated.
const problem = error || diagnostic?.error;
const shownProblem = problem ? t(problem) : null;
const shownHost = hostStatus ? t(hostStatus) : null;
const b = manifest?.bounds;
const canvas = viewer?.scene?.canvas;
const point =
@@ -618,9 +626,8 @@ export function createWeatherLayer({
detail,
status:
missing ||
hostStatus ||
error ||
diagnostic?.error ||
shownHost ||
shownProblem ||
(observationDelayed()
? t('Source observations delayed')
: manifest?.stale
@@ -701,8 +708,8 @@ export function createWeatherLayer({
}))
: [],
info: hostHidden
? hostStatus
: `${infoHeading}\n${infoTime}${missing ? `\n${missing}` : ''}${manifest?.stale ? `\n${t('STALE · cached source metadata')}` : ''}${error || diagnostic?.error ? '\n' + (error || diagnostic.error) : ''}\n${infoCoverage}${outside ? `\n${t('Map center is outside source coverage')}` : ''}${infoMotion ? `\n${infoMotion}` : ''}`,
? shownHost
: `${infoHeading}\n${infoTime}${missing ? `\n${missing}` : ''}${manifest?.stale ? `\n${t('STALE · cached source metadata')}` : ''}${shownProblem ? `\n${shownProblem}` : ''}\n${infoCoverage}${outside ? `\n${t('Map center is outside source coverage')}` : ''}${infoMotion ? `\n${infoMotion}` : ''}`,
infoTitle: lightning
? t(
'NOAA/NWS 15-minute lightning density derived from Vaisala NLDN/GLD360. Coverage 110°E across the Pacific/Americas to 0°, 25°S–80°N. Not a live strike count, global coverage or a safety warning.',
+5 -2
View File
@@ -3,6 +3,7 @@ import { orderWeatherImagery } from './imageryOrder.js';
import { imageryHostStatus } from './imageryHost.js';
import { createRasterTileProvider } from './rasterTiles.js';
import { createWeatherShell } from './shellRendering.js';
import { msg } from '../../i18n/index.js';
import { readResponseBytesCapped } from '../../sources/httpBody.js';
import {
acquireInfraredMosaic,
@@ -294,7 +295,9 @@ function createGlobeRendering({
);
}
} else {
lastError = 'Weather tiles unavailable · previous frame retained';
lastError = msg(
'Weather tiles unavailable · previous frame retained',
);
remove(frame);
}
frame.resolve(ok);
@@ -413,7 +416,7 @@ function createGlobeRendering({
return;
}
frame.failed = true;
lastError = 'Some weather tiles unavailable';
lastError = msg('Some weather tiles unavailable');
onChange();
});
// A shown, transparent layer lets Cesium request staging tiles while the last
+2 -1
View File
@@ -1,6 +1,7 @@
import { acquireWeatherImage } from './infraredImage.js';
import { imageryHostStatus } from './imageryHost.js';
import { WEATHER_DETAIL_SIZE, WEATHER_IMAGE_SIZES } from './source.js';
import { msg } from '../../i18n/index.js';
/** Metres above the ellipsoid. Lower shells draw first, so lightning draws last. */
export const WEATHER_SHELL_HEIGHTS = Object.freeze({
@@ -630,7 +631,7 @@ export function createWeatherShell({
if (incoming !== frame) return;
incoming = null;
close(frame);
lastError = 'Weather tiles unavailable · previous frame retained';
lastError = msg('Weather tiles unavailable · previous frame retained');
frame.resolve(false);
scene.requestRender();
onChange();