i18n(it): address code review findings

Localize a bootstrap shot title without a source pack only when the
Nepal pack marker binds that title to the shot's id, so an operator
shot renamed to a bootstrap title in the built-in scene stays as typed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-29 14:54:44 +04:00
1 parent d383f25bb3
commit f8e96e1484
4 files changed
+57 -10

No files matched your search

@@ -0,0 +1,20 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { SCENE_RECIPES, displayShotTitle } from './recipes.js';
import { setLocale } from '../i18n/index.js';
test('an operator-typed title in the built-in Nepal scene stays as typed', () => {
const scene = SCENE_RECIPES.find((r) => r.id === 'bhote-koshi-nepal-scene');
assert.ok(scene, 'built-in bootstrap scene exists');
for (const locale of ['ru', 'es', 'it', 'zh-CN', 'zh-Hant']) {
setLocale(locale);
const title = 'Global Incident Context';
// A shot captured by the operator: no sourcePackId, renamed to this title.
const shown = displayShotTitle(
title,
{ id: 'shot-user', title },
{ id: scene.id },
);
assert.equal(shown, title, `${locale}: got ${shown}`);
}
});
+4
View File
@@ -712,6 +712,10 @@ test('a legacy three-shot Nepal browser project bootstraps to the current 25-sho
assert.deepEqual(scene.shots.slice(0, 3).map(({ camera }) => camera), originalCameras); assert.deepEqual(scene.shots.slice(0, 3).map(({ camera }) => camera), originalCameras);
assert.equal(scene.appliedShotPacks[0].id, 'bhote-koshi-nepal-evidence-pack'); assert.equal(scene.appliedShotPacks[0].id, 'bhote-koshi-nepal-evidence-pack');
assert.equal(scene.appliedShotPacks[0].version, 18); assert.equal(scene.appliedShotPacks[0].version, 18);
// displayShotTitle trusts these bindings to localize the bootstrap shots.
for (const shot of scene.shots.slice(0, 8)) {
assert.equal(scene.appliedShotPacks[0].shotBindings[shot.title], shot.id);
}
assert.equal(director._selectedSceneId, scene.id); assert.equal(director._selectedSceneId, scene.id);
assert.equal(director._selectedShotId, scene.shots[0].id); assert.equal(director._selectedShotId, scene.shots[0].id);
} finally { } finally {
+19 -8
View File
@@ -1061,33 +1061,44 @@ export function displaySceneTitle(scene) {
* (`sourcePackId`), so a typed title that matches a built-in one stays as * (`sourcePackId`), so a typed title that matches a built-in one stays as
* typed. Without `shot`, the title alone decides. * typed. Without `shot`, the title alone decides.
* @param {string} title * @param {string} title
* @param {{sourcePackId?: string}} [shot] * @param {{id?: string, sourcePackId?: string}} [shot]
* @param {{id?: string}} [scene] The shot's scene, for projects saved before * @param {{id?: string, appliedShotPacks?: Array<{id?: string, shotBindings?: Object<string, string>}>}} [scene]
* shots recorded their source pack. * The shot's scene, for projects saved before shots recorded their source
* pack.
* @returns {string} * @returns {string}
*/ */
export function displayShotTitle(title, shot = undefined, scene = undefined) { export function displayShotTitle(title, shot = undefined, scene = undefined) {
const text = typeof title === 'string' ? title : ''; const text = typeof title === 'string' ? title : '';
const fromRecipe = shot const fromRecipe = shot
? Boolean(shot.sourcePackId) || recipeDeclaresShotTitle(scene?.id, text) ? Boolean(shot.sourcePackId) || recipeDeclaresShotTitle(scene, shot, text)
: true; : true;
if (fromRecipe && BUILTIN_SHOT_TITLES.has(text)) return t(text); if (fromRecipe && BUILTIN_SHOT_TITLES.has(text)) return t(text);
const numbered = /^Shot ([1-9]\d*)$/.exec(text); const numbered = /^Shot ([1-9]\d*)$/.exec(text);
return numbered ? t('Shot {n}', { n: numbered[1] }) : text; return numbered ? t('Shot {n}', { n: numbered[1] }) : text;
} }
/** Whether the built-in recipe behind a scene declares a shot with this title. */ /** Whether the built-in recipe behind a scene declares this shot's title. */
function recipeDeclaresShotTitle(sceneId, title) { function recipeDeclaresShotTitle(scene, shot, title) {
const recipe = sceneId ? getSceneRecipeById(sceneId) : null; const recipe = scene?.id ? getSceneRecipeById(scene.id) : null;
if (!recipe) return false; if (!recipe) return false;
const declares = (path) => const declares = (path) =>
Array.isArray(path) && path.some((keyframe) => keyframe?.title === title); Array.isArray(path) && path.some((keyframe) => keyframe?.title === title);
if (declares(recipe.cameraPath)) return true; if (declares(recipe.cameraPath)) return true;
// A bootstrap recipe seeds legacy "Shot N" keyframes; the upgrade replaces // A bootstrap recipe seeds legacy "Shot N" keyframes; the upgrade replaces
// them with the append recipe's bootstrap shots, which carry no source pack. // them with the append recipe's bootstrap shots, which carry no source pack.
// The pack marker binds each of those titles to its shot id, so a shot the
// operator later gave the same title stays as typed.
return SCENE_APPEND_RECIPES.some( return SCENE_APPEND_RECIPES.some(
(append) => (append) =>
append.legacySceneBootstrap?.targetSceneTitle === recipe.title && append.legacySceneBootstrap?.targetSceneTitle === recipe.title &&
declares(append.legacySceneBootstrap.cameraPath), declares(append.legacySceneBootstrap.cameraPath) &&
Boolean(shot?.id) &&
(Array.isArray(scene.appliedShotPacks)
? scene.appliedShotPacks
: []
).some(
(marker) =>
marker?.id === append.id && marker.shotBindings?.[title] === shot.id,
),
); );
} }
+14 -2
View File
@@ -155,11 +155,23 @@ test('bootstrap shots of a built-in scene localize without a source pack', () =>
); );
if (!scene) return; if (!scene) return;
for (const { title } of bootstrap.cameraPath) { for (const { title } of bootstrap.cameraPath) {
// The upgrade copies these keyframes into the scene without sourcePackId. // The upgrade copies these keyframes into the scene without sourcePackId
// and binds each title to its shot in the pack marker.
const upgraded = {
id: scene.id,
appliedShotPacks: [
{ id: append.id, version: 1, shotBindings: { [title]: 'shot-old' } },
],
};
assert.equal( assert.equal(
displayShotTitle(title, { id: 'shot-old', title }, { id: scene.id }), displayShotTitle(title, { id: 'shot-old', title }, upgraded),
displayShotTitle(title), displayShotTitle(title),
); );
// Another shot the operator gave the same title stays as typed.
assert.equal(
displayShotTitle(title, { id: 'shot-user', title }, upgraded),
title,
);
// The same title in an operator's own scene stays as typed. // The same title in an operator's own scene stays as typed.
assert.equal( assert.equal(
displayShotTitle(title, { id: 'shot-user', title }, { id: 'scene-user' }), displayShotTitle(title, { id: 'shot-user', title }, { id: 'scene-user' }),