fix(i18n): keep English pixel-identical and stored data English

From the English-parity and breakage review against main:
- English wording back to main: "Configured Source", the "SHARED SUBJECT"
  fallback, CCTV calibration "RANGE 120m".
- Voice header and CCTV calibration chip/select tweaks for long labels now
  apply to non-English only (html:not([lang='en'])); English matches main.
  The scene select font change is scoped the same way.
- New scenes and shots store their default title in English ("Scene 3",
  "Shot 2") and translate it when shown, so a title captured in one language
  does not leak into projects, storage or shared JSON.
- Choosing a language with storage blocked keeps it in ?lang instead of
  silently doing nothing.
- One picked import file shows its name again.
- qa-l9-matrix reads the voice state from data-status, not the label.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-13 20:03:54 +04:00
1 parent a28e29e05e
commit 9d01f9bfcd
15 files changed
+118 -54

No files matched your search

+25
View File
@@ -188,6 +188,31 @@ test('applyDocumentTranslations translates opted-in text and attributes in place
assert.equal(unknown.textContent, 'No such message');
});
test('persistLocaleAndReload keeps the choice in ?lang when storage is blocked', () => {
const calls = [];
const location = {
href: 'https://gev.local/?x=1#scene=a',
assign: (url) => calls.push(['assign', url]),
reload: () => calls.push(['reload']),
};
const history = {
replaceState: (_s, _t, url) => calls.push(['replace', url]),
};
const storage = {
setItem() {
throw new Error('blocked');
},
};
assert.equal(
persistLocaleAndReload('ru', { location, history, storage }),
true,
);
assert.deepEqual(calls, [
['replace', 'https://gev.local/?x=1&lang=ru#scene=a'],
['reload'],
]);
});
test('persistLocaleAndReload stores the choice, keeps the hash, strips ?lang', () => {
const storage = memoryStorage();
const calls = [];
+5 -3
View File
@@ -212,7 +212,7 @@ export function applyDocumentTranslations(doc) {
/**
* Persist a locale choice, then reload preserving window.location.hash exactly
* and stripping ?lang from the search string, so the override is one-shot.
* Storage is best-effort: where it is blocked the choice will not survive.
* Where storage is blocked, ?lang carries the choice instead.
* @returns {boolean} true when a reload was triggered.
*/
export function persistLocaleAndReload(
@@ -224,12 +224,14 @@ export function persistLocaleAndReload(
} = {},
) {
const normalized = normalizeLocale(locale) || DEFAULT_LOCALE;
writeStoredLocale(normalized, storage);
const stored = writeStoredLocale(normalized, storage);
if (!location || typeof location.assign !== 'function') return false;
let target = location.href;
try {
// Without storage the choice only survives in the URL, so keep it there.
const url = new URL(location.href);
url.searchParams.delete('lang');
if (stored) url.searchParams.delete('lang');
else url.searchParams.set('lang', normalized);
target = url.href;
} catch {
// Keep the current href.
+4 -1
View File
@@ -2025,5 +2025,8 @@
"Uses registered content or linked media. Those external files are not included in a scene bundle. Their original notices still apply.": "Usa contenido registrado o medios vinculados. Esos archivos externos no se incluyen en un paquete de escena. Sus avisos originales siguen vigentes.",
"{state} {layers}": "{state}: {layers}",
"Selected Space Mission": "Misión espacial seleccionada",
"{duration}s + {hold}s": "{duration} s + {hold} s"
"{duration}s + {hold}s": "{duration} s + {hold} s",
"Configured Source": "Fuente configurada",
"subject": "sujeto",
"{value}m": "{value} m"
}
+4 -1
View File
@@ -2057,5 +2057,8 @@
"Uses registered content or linked media. Those external files are not included in a scene bundle. Their original notices still apply.": "Используется зарегистрированный контент или связанные медиафайлы. Эти внешние файлы не входят в архив сцены. Их исходные уведомления по-прежнему действуют.",
"{state} {layers}": "{state}: {layers}",
"Selected Space Mission": "Выбранная космическая миссия",
"{duration}s + {hold}s": "{duration} с + {hold} с"
"{duration}s + {hold}s": "{duration} с + {hold} с",
"Configured Source": "Настроенный источник",
"subject": "объект",
"{value}m": "{value} м"
}