From d034e7355d27d3032702c3c91bcfcc942cc8cbc6 Mon Sep 17 00:00:00 2001 From: German Khokhlov Date: Thu, 29 Oct 2026 14:48:09 +0400 Subject: [PATCH] i18n(fr): address code review findings Localize a pack shot title only for the shot the pack marker binds to that title, so an operator's shot named like a pack shot shows as typed. Markers saved without shotBindings keep the title-only check. Co-Authored-By: Claude Opus 5.5 --- src/scenes/director.test.mjs | 23 ++++++++++++++++++++++- src/scenes/recipes.js | 18 ++++++++++++------ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/scenes/director.test.mjs b/src/scenes/director.test.mjs index fd8db6149..880c25bdf 100644 --- a/src/scenes/director.test.mjs +++ b/src/scenes/director.test.mjs @@ -17,7 +17,8 @@ import test from 'node:test'; import { LayerLifecycle } from '../data/lifecycle.js'; import { SceneDirector } from './director.js'; import { SCENE_TRACKING_PARAM_KEYS } from './scenePolicy.js'; -import { SCENE_RECIPES, getSceneAppendRecipeById } from './recipes.js'; +import { SCENE_RECIPES, displayShotTitle, getSceneAppendRecipeById } from './recipes.js'; +import { setLocale } from '../i18n/index.js'; const NEPAL_ORIGINAL_SHOT_TITLES = [ 'Global Incident Context', @@ -2019,3 +2020,23 @@ test('a caught run failure resolves as run-failed, not success (#971)', async () ); } finally { console.warn = warn; await director.destroy(); restore(); } }); + +test('a user shot whose title matches an applied pack title keeps the typed title', () => { + const { director, restore } = makeDirector({ project: legacyThreeShotNepalProjectFixture() }); + try { + const scene = director._project.scenes[0]; + setLocale('ru'); + // The bootstrap-rebuilt shot (no sourcePackId, bound by the pack marker) still localizes. + const bootstrap = scene.shots[0]; + assert.equal(bootstrap.sourcePackId, undefined); + assert.notEqual(displayShotTitle(bootstrap.title, bootstrap, scene), bootstrap.title); + // A shot the operator added and named like a pack shot shows as typed. + for (const title of ['Final view', 'Global Incident Context']) { + const userShot = { id: `user-shot-${title}`, title }; + assert.equal(displayShotTitle(title, userShot, scene), title); + } + } finally { + setLocale('en'); + restore(); + } +}); diff --git a/src/scenes/recipes.js b/src/scenes/recipes.js index 0bb1d9e94..963dbde6c 100644 --- a/src/scenes/recipes.js +++ b/src/scenes/recipes.js @@ -1069,7 +1069,7 @@ export function displaySceneTitle(scene) { export function displayShotTitle(title, shot = undefined, scene = undefined) { const text = typeof title === 'string' ? title : ''; const fromRecipe = shot - ? Boolean(shot.sourcePackId) || recipeDeclaresShotTitle(scene, text) + ? Boolean(shot.sourcePackId) || recipeDeclaresShotTitle(scene, text, shot) : true; if (fromRecipe && BUILTIN_SHOT_TITLES.has(text)) return t(text); const numbered = /^Shot ([1-9]\d*)$/.exec(text); @@ -1080,9 +1080,11 @@ export function displayShotTitle(title, shot = undefined, scene = undefined) { * Whether the built-in recipe behind a scene, or a shot pack applied to it, * declares a shot with this title. A pack's legacy bootstrap rebuilds the * first shots of an upgraded scene without a sourcePackId, so the pack's - * required titles vouch for them. + * required titles vouch for them: through the marker's shotBindings when it + * has them, so an operator's shot named like a pack shot stays as typed, and + * by title alone for markers saved before bindings. */ -function recipeDeclaresShotTitle(scene, title) { +function recipeDeclaresShotTitle(scene, title, shot) { const recipe = scene?.id ? getSceneRecipeById(scene.id) : null; if (recipe?.cameraPath?.some((keyframe) => keyframe?.title === title)) return true; @@ -1090,8 +1092,12 @@ function recipeDeclaresShotTitle(scene, title) { ? scene.appliedShotPacks : []; return packs.some((entry) => - Boolean( - getSceneAppendRecipeById(entry?.id)?.requiredShotTitles?.includes(title), - ), + entry?.shotBindings + ? entry.shotBindings[title] === shot?.id + : Boolean( + getSceneAppendRecipeById(entry?.id)?.requiredShotTitles?.includes( + title, + ), + ), ); }