mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
refactor(i18n): key messages by English text; add Russian
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]>
This commit is contained in:
1 parent
dfbaeafbd8
commit
13efd411b6
112 files changed
+4419
-4856
No files matched your search
+20
-18
@@ -26,7 +26,7 @@ import {
|
||||
getSceneAppendRecipeById,
|
||||
} from './recipes.js';
|
||||
import { sceneLayerPlan, sceneRequiresContextModeExit } from './scenePolicy.js';
|
||||
import { t } from '../i18n/index.js';
|
||||
import { t, msg } from '../i18n/index.js';
|
||||
import { createSceneDataPacks } from './dataPacks/controller.js';
|
||||
import { createSceneInteractions } from './interactions.js';
|
||||
import { createSceneSharing } from './sharing.js';
|
||||
@@ -67,11 +67,11 @@ import {
|
||||
// boundary + persisted data); only the presentation of an UNRENAMED built-in
|
||||
// localizes, so an operator's renamed scene is never masked by the catalog.
|
||||
const RECIPE_TITLE_KEYS = Object.freeze({
|
||||
'flights-radar': 'setup.scenes.recipe.flightsRadar',
|
||||
'orbital-watch': 'setup.scenes.recipe.orbitalWatch',
|
||||
'thermal-threats': 'setup.scenes.recipe.thermalThreats',
|
||||
'city-overload': 'setup.scenes.recipe.cityOverload',
|
||||
'omniscience-pullback': 'setup.scenes.recipe.omnisciencePullback',
|
||||
'flights-radar': msg('Global Flights Radar'),
|
||||
'orbital-watch': msg('Orbital Watch'),
|
||||
'thermal-threats': msg('Thermal Threat Board'),
|
||||
'city-overload': msg('City Overload'),
|
||||
'omniscience-pullback': msg('Omniscience Pullback'),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -198,7 +198,7 @@ export class SceneDirector {
|
||||
this._presentation = {
|
||||
status: this._storageReadError
|
||||
? 'Saved project could not be read; storage preserved. Import a valid file to resume saving.'
|
||||
: t('setup.scenes.statusReady'),
|
||||
: t('Ready'),
|
||||
progress: 0,
|
||||
runtime: '',
|
||||
playbackActive: false,
|
||||
@@ -396,7 +396,7 @@ export class SceneDirector {
|
||||
|
||||
_shotOutcome(type, scene, shot, index = scene.shots.indexOf(shot)) {
|
||||
if (type === 'shot-loaded')
|
||||
this._presentation.status = t('setup.scenes.status.loaded', {
|
||||
this._presentation.status = t('Loaded: {scene} / {shot}', {
|
||||
scene: scene.title,
|
||||
shot: shot.title,
|
||||
});
|
||||
@@ -954,14 +954,14 @@ export class SceneDirector {
|
||||
|
||||
const camera = this.styleManager.getCameraState();
|
||||
if (!camera) {
|
||||
this._updateStatus(t('setup.scenes.status.captureCameraNotReady'));
|
||||
this._updateStatus(t('Cannot capture shot: camera not ready'));
|
||||
return;
|
||||
}
|
||||
|
||||
const shot = normalizeShot(
|
||||
{
|
||||
id: uid('shot'),
|
||||
title: t('setup.scenes.status.shotTitleDefault', {
|
||||
title: t('Shot {n}', {
|
||||
n: scene.shots.length + 1,
|
||||
}),
|
||||
durationSec: DEFAULT_SHOT_DURATION_SEC,
|
||||
@@ -978,7 +978,7 @@ export class SceneDirector {
|
||||
this._saveProject();
|
||||
this._shotOutcome('shot-captured', scene, shot);
|
||||
this._updateStatus(
|
||||
t('setup.scenes.status.captured', {
|
||||
t('Captured: {scene} / {shot}', {
|
||||
scene: scene.title,
|
||||
shot: shot.title,
|
||||
}),
|
||||
@@ -995,7 +995,7 @@ export class SceneDirector {
|
||||
|
||||
const shot = scene.shots.find((item) => item.id === this._selectedShotId);
|
||||
if (!shot) {
|
||||
this._updateStatus(t('setup.scenes.status.selectShotFirst'));
|
||||
this._updateStatus(t('Select a shot first'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1011,7 +1011,7 @@ export class SceneDirector {
|
||||
this._saveProject();
|
||||
this._shotOutcome('shot-updated', scene, shot);
|
||||
this._updateStatus(
|
||||
t('setup.scenes.status.updated', {
|
||||
t('Updated: {scene} / {shot}', {
|
||||
scene: scene.title,
|
||||
shot: shot.title,
|
||||
}),
|
||||
@@ -1610,7 +1610,7 @@ export class SceneDirector {
|
||||
this._claimingCamera = false;
|
||||
}
|
||||
if (claimed === false) {
|
||||
this._updateStatus(t('setup.scenes.status.cameraUnavailable'));
|
||||
this._updateStatus(t('Camera unavailable — exit cockpit first'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1745,7 +1745,7 @@ export class SceneDirector {
|
||||
if (!queue.length) return { started: false, reason: 'scene-complete' };
|
||||
}
|
||||
if (!queue.length) {
|
||||
this._updateStatus(t('setup.scenes.status.noShotsToRun'));
|
||||
this._updateStatus(t('No shots to run'));
|
||||
return { started: false, reason: 'no-shots' };
|
||||
}
|
||||
|
||||
@@ -1829,7 +1829,7 @@ export class SceneDirector {
|
||||
});
|
||||
} catch (error) {
|
||||
this._updateStatus(
|
||||
t('setup.scenes.status.runError', {
|
||||
t('Error: {message}', {
|
||||
message: error.message || 'run failed',
|
||||
}),
|
||||
);
|
||||
@@ -2006,7 +2006,7 @@ export class SceneDirector {
|
||||
link.click();
|
||||
link.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
this._presentation.status = t('setup.scenes.status.projectExported');
|
||||
this._presentation.status = t('Project exported');
|
||||
this._publish({ type: 'project-exported', project: this._project });
|
||||
}
|
||||
|
||||
@@ -2268,7 +2268,9 @@ export class SceneDirector {
|
||||
`[Scenes] Could not exit ${mode}:`,
|
||||
result.error || 'unknown reason',
|
||||
);
|
||||
this._updateStatus(t('setup.scenes.status.contextExitFailed', { mode }));
|
||||
this._updateStatus(
|
||||
t('Could not exit {mode} — scene layers may be refused', { mode }),
|
||||
);
|
||||
this._logEvent('context_mode_exit_failed', {
|
||||
mode,
|
||||
error: result.error || null,
|
||||
|
||||
@@ -16,7 +16,7 @@ export function createScenePlaybackAdapter(director, defaultShotDurationSec) {
|
||||
director._renderSceneSelect();
|
||||
director._renderShotList();
|
||||
director._updateStatus(
|
||||
t('setup.scenes.status.runningShot', {
|
||||
t('Running {index}/{total}: {scene} / {shot}', {
|
||||
index: index + 1,
|
||||
total,
|
||||
scene: scene.title,
|
||||
@@ -78,7 +78,7 @@ export function createScenePlaybackAdapter(director, defaultShotDurationSec) {
|
||||
},
|
||||
complete() {
|
||||
director._setProgress(1);
|
||||
director._updateStatus(t('setup.scenes.status.runComplete'));
|
||||
director._updateStatus(t('Scene run complete'));
|
||||
director._logEvent('scene_run_complete', {});
|
||||
},
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user