mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
Replace the semantic keys with the English source text, so code reads as the
UI does and English needs no catalog: t('Save keys'), tn(count, one, other)
for plurals, msg() for text stored in data, and bare data-i18n* markup that
translates the element's own English. Other languages are one flat JSON each.
- Simplify src/i18n: drop namespaces, the configurable locale pair
(GEV_DEFAULT_LOCALE / GEV_SECONDARY_LOCALE) and its build defines. Locale
comes from ?lang=, the stored choice, then browser languages.
- Add scripts/i18n.mjs (npm run i18n): extracts every message from t/tn/msg
and markup, and fails on missing, unused, placeholder-mismatched or
wrongly-shaped plural entries. npm test runs it via catalog.test.mjs.
- Add Russian (src/i18n/locales/ru.json, 833 messages), started from the
community catalog in #312 and reviewed against the current English.
- Fix data-i18n placed on parents that would wipe their children (cockpit
LEVEL and local weather cells, first-run EXPLORE MANUALLY tile), and a
doubled degree sign in the TRACK ACQUIRED signal.
- Rewrite docs/TRANSLATORS.md for the new workflow; drop sections the earlier
merge resurrected in docs/CURRENT-STATE.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
132 lines
4.5 KiB
JavaScript
132 lines
4.5 KiB
JavaScript
import { createApplicationOperations } from './operations.js';
|
|
import * as Cesium from 'cesium';
|
|
import {
|
|
createApplicationViewer,
|
|
installTrackpadPinchZoom,
|
|
} from '../app/viewer.js';
|
|
import { registerDataCredits } from '../data/dataCredits.js';
|
|
import { configureCreditKeyboardAccess } from '../creditKeyboard.js';
|
|
import { MapStackController } from '../mapStackController.js';
|
|
import { loadPhotorealisticTileset } from '../mapStartup.js';
|
|
import { initLogoGaze } from '../logoGaze.js';
|
|
import {
|
|
uninstallRenderGovernor,
|
|
governorRequestRender,
|
|
} from '../renderGovernor.js';
|
|
import { describeError } from './errors.js';
|
|
import { t } from '../i18n/index.js';
|
|
|
|
/** Construct the application globe using the caller's local configuration. */
|
|
export async function createApplicationScene({
|
|
requestServices,
|
|
googleApiKey,
|
|
googleTokens = null,
|
|
cesiumToken,
|
|
credits,
|
|
MapController = MapStackController,
|
|
mapOptions = {},
|
|
loaderStatus,
|
|
signal,
|
|
defer,
|
|
}) {
|
|
const operations = createApplicationOperations({
|
|
requests: requestServices,
|
|
signal,
|
|
});
|
|
defer(initLogoGaze());
|
|
const previousKey = window.__GOOGLE_MAPS_API_KEY__;
|
|
if (googleApiKey) {
|
|
window.__GOOGLE_MAPS_API_KEY__ = googleApiKey;
|
|
defer(() => {
|
|
if (window.__GOOGLE_MAPS_API_KEY__ !== googleApiKey) return;
|
|
if (previousKey === undefined) delete window.__GOOGLE_MAPS_API_KEY__;
|
|
else window.__GOOGLE_MAPS_API_KEY__ = previousKey;
|
|
});
|
|
}
|
|
loaderStatus.textContent = t('Configuring viewer...');
|
|
// Provider attribution stays visible, including clean-view and recording.
|
|
const creditContainer = document.createElement('div');
|
|
creditContainer.id = 'cesium-credits';
|
|
document.body.appendChild(creditContainer);
|
|
defer(() => creditContainer.remove());
|
|
const viewer = createApplicationViewer({
|
|
container: 'cesiumContainer',
|
|
creditContainer,
|
|
});
|
|
defer(() => {
|
|
uninstallRenderGovernor(viewer);
|
|
if (!viewer.isDestroyed()) viewer.destroy();
|
|
});
|
|
defer(installTrackpadPinchZoom(viewer));
|
|
registerDataCredits(viewer, credits);
|
|
configureCreditKeyboardAccess(document);
|
|
loaderStatus.textContent =
|
|
googleApiKey || googleTokens || cesiumToken
|
|
? t('Loading Google 3D Tiles...')
|
|
: t('Loading the keyless globe...');
|
|
const photoreal = await loadPhotorealisticTileset(Cesium, {
|
|
googleApiKey,
|
|
googleTokens,
|
|
cesiumToken,
|
|
});
|
|
const tileset = photoreal.tileset;
|
|
// A provider can finish after cancellation; retain ownership of its result.
|
|
defer(() => {
|
|
if (tileset && !tileset.isDestroyed()) {
|
|
if (!viewer.scene.primitives.remove(tileset)) tileset.destroy();
|
|
}
|
|
});
|
|
signal.throwIfAborted();
|
|
if (tileset) {
|
|
viewer.scene.primitives.add(tileset);
|
|
// NOTE: Cesium World Terrain intentionally disabled — conflicts with Google 3D Tiles at high zoom.
|
|
// Google Photorealistic 3D Tiles provide their own terrain/elevation.
|
|
viewer.scene.globe.show = false;
|
|
console.info(`[Init] Google 3D Tiles loaded via ${photoreal.route}.`);
|
|
} else {
|
|
if (photoreal.errors.length) {
|
|
const tileError = photoreal.errors.at(-1);
|
|
console.warn(
|
|
'[Init] Google 3D Tiles unavailable, using the keyless globe:',
|
|
tileError,
|
|
);
|
|
const tileErrorDetail = describeError(tileError);
|
|
loaderStatus.textContent = t(
|
|
'Google 3D Tiles unavailable ({detail}). Loading the keyless globe...',
|
|
{
|
|
detail: tileErrorDetail,
|
|
},
|
|
);
|
|
}
|
|
viewer.scene.globe.show = true;
|
|
}
|
|
|
|
loaderStatus.textContent = t('Initializing systems...');
|
|
|
|
const mapStackController = new MapController(viewer, {
|
|
requestRender: governorRequestRender,
|
|
...mapOptions,
|
|
googleTileset: tileset,
|
|
cesiumToken,
|
|
initialStack: tileset ? 'photoreal' : 'esri-imagery',
|
|
// Task 5 (height-datum fix): rebroadcast stack changes as a window
|
|
// CustomEvent so data layers (CCTV per-regime ground resolution) can
|
|
// react without coupling MapStackController to layer modules. Fires on
|
|
// 'switching'/'ready'/'error'; listeners derive the surface regime from
|
|
// live scene state, so intermediate emissions are harmless.
|
|
onChange: (state) => {
|
|
window.dispatchEvent(
|
|
new CustomEvent('gev:map-stack-changed', { detail: state }),
|
|
);
|
|
},
|
|
onError: (message) => console.warn('[MapStack]', message),
|
|
});
|
|
defer(() => mapStackController.destroy());
|
|
await mapStackController.setStack(tileset ? 'photoreal' : 'esri-imagery', {
|
|
silent: true,
|
|
});
|
|
|
|
signal.throwIfAborted();
|
|
return { viewer, tileset, mapStackController, operations };
|
|
}
|