diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index b659cc7145ce01bc2a7e00060953f39ddc703730..45bf9ec48ae01f58589dedfe3501d88c88eadfe4 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -339,7 +339,7 @@ class Blocks extends React.Component { const targetCostumes = target.getCostumes(); const targetSounds = target.getSounds(); const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); - return makeToolboxXML(target.isStage, target.id, dynamicBlocksXML, + return makeToolboxXML(false, target.isStage, target.id, dynamicBlocksXML, targetCostumes[targetCostumes.length - 1].name, stageCostumes[stageCostumes.length - 1].name, targetSounds.length > 0 ? targetSounds[targetSounds.length - 1].name : '' diff --git a/src/lib/make-toolbox-xml.js b/src/lib/make-toolbox-xml.js index 79cf35cc25e872931383a3dc3d0ceb955664812a..7d1257574235d35284c57e55933287af378f3450 100644 --- a/src/lib/make-toolbox-xml.js +++ b/src/lib/make-toolbox-xml.js @@ -4,7 +4,7 @@ const categorySeparator = '<sep gap="36"/>'; const blockSeparator = '<sep gap="36"/>'; // At default scale, about 28px -const motion = function (isStage, targetId) { +const motion = function (_, isStage, targetId) { const stageSelected = ScratchBlocks.ScratchMsgs.translate( 'MOTION_STAGE_SELECTED', 'Stage selected: no motion blocks' @@ -150,7 +150,7 @@ const xmlEscape = function (unsafe) { }); }; -const looks = function (isStage, targetId, costumeName, backdropName) { +const looks = function (_, isStage, targetId, costumeName, backdropName) { const hello = ScratchBlocks.ScratchMsgs.translate('LOOKS_HELLO', 'Hello!'); const hmm = ScratchBlocks.ScratchMsgs.translate('LOOKS_HMM', 'Hmm...'); return ` @@ -287,7 +287,7 @@ const looks = function (isStage, targetId, costumeName, backdropName) { `; }; -const sound = function (isStage, targetId, soundName) { +const sound = function (_, isStage, targetId, soundName) { return ` <category name="%{BKY_CATEGORY_SOUND}" id="sound" colour="#D65CD6" secondaryColour="#BD42BD"> <block id="${targetId}_sound_playuntildone" type="sound_playuntildone"> @@ -342,7 +342,7 @@ const sound = function (isStage, targetId, soundName) { `; }; -const events = function (isStage) { +const events = function (_, isStage) { return ` <category name="%{BKY_CATEGORY_EVENTS}" id="events" colour="#FFD500" secondaryColour="#CC9900"> <block type="event_whenflagclicked"/> @@ -381,7 +381,7 @@ const events = function (isStage) { `; }; -const control = function (isStage) { +const control = function (_, isStage) { return ` <category name="%{BKY_CATEGORY_CONTROL}" id="control" colour="#FFAB19" secondaryColour="#CF8B17"> <block type="control_wait"> @@ -428,7 +428,7 @@ const control = function (isStage) { `; }; -const sensing = function (isStage) { +const sensing = function (isInitialSetup, isStage) { const name = ScratchBlocks.ScratchMsgs.translate('SENSING_ASK_TEXT', 'What\'s your name?'); return ` <category name="%{BKY_CATEGORY_SENSING}" id="sensing" colour="#4CBFE6" secondaryColour="#2E8EB8"> @@ -458,13 +458,15 @@ const sensing = function (isStage) { </block> ${blockSeparator} `} - <block id="askandwait" type="sensing_askandwait"> - <value name="QUESTION"> - <shadow type="text"> - <field name="TEXT">${name}</field> - </shadow> - </value> - </block> + ${isInitialSetup ? '' : ` + <block id="askandwait" type="sensing_askandwait"> + <value name="QUESTION"> + <shadow type="text"> + <field name="TEXT">${name}</field> + </shadow> + </value> + </block> + `} <block id="answer" type="sensing_answer"/> ${blockSeparator} <block type="sensing_keypressed"> @@ -501,7 +503,7 @@ const sensing = function (isStage) { `; }; -const operators = function () { +const operators = function (isInitialSetup) { const apple = ScratchBlocks.ScratchMsgs.translate('OPERATORS_JOIN_APPLE', 'apple'); const banana = ScratchBlocks.ScratchMsgs.translate('OPERATORS_JOIN_BANANA', 'banana'); const letter = ScratchBlocks.ScratchMsgs.translate('OPERATORS_LETTEROF_APPLE', 'a'); @@ -610,49 +612,51 @@ const operators = function () { <block type="operator_or"/> <block type="operator_not"/> ${blockSeparator} - <block type="operator_join"> - <value name="STRING1"> - <shadow type="text"> - <field name="TEXT">${apple} </field> - </shadow> - </value> - <value name="STRING2"> - <shadow type="text"> - <field name="TEXT">${banana}</field> - </shadow> - </value> - </block> - <block type="operator_letter_of"> - <value name="LETTER"> - <shadow type="math_whole_number"> - <field name="NUM">1</field> - </shadow> - </value> - <value name="STRING"> + ${isInitialSetup ? '' : ` + <block type="operator_join"> + <value name="STRING1"> + <shadow type="text"> + <field name="TEXT">${apple} </field> + </shadow> + </value> + <value name="STRING2"> + <shadow type="text"> + <field name="TEXT">${banana}</field> + </shadow> + </value> + </block> + <block type="operator_letter_of"> + <value name="LETTER"> + <shadow type="math_whole_number"> + <field name="NUM">1</field> + </shadow> + </value> + <value name="STRING"> + <shadow type="text"> + <field name="TEXT">${apple}</field> + </shadow> + </value> + </block> + <block type="operator_length"> + <value name="STRING"> + <shadow type="text"> + <field name="TEXT">${apple}</field> + </shadow> + </value> + </block> + <block type="operator_contains" id="operator_contains"> + <value name="STRING1"> <shadow type="text"> - <field name="TEXT">${apple}</field> + <field name="TEXT">${apple}</field> </shadow> - </value> - </block> - <block type="operator_length"> - <value name="STRING"> + </value> + <value name="STRING2"> <shadow type="text"> - <field name="TEXT">${apple}</field> + <field name="TEXT">${letter}</field> </shadow> - </value> - </block> - <block type="operator_contains" id="operator_contains"> - <value name="STRING1"> - <shadow type="text"> - <field name="TEXT">${apple}</field> - </shadow> - </value> - <value name="STRING2"> - <shadow type="text"> - <field name="TEXT">${letter}</field> - </shadow> - </value> - </block> + </value> + </block> + `} ${blockSeparator} <block type="operator_mod"> <value name="NUM1"> @@ -714,7 +718,10 @@ const xmlOpen = '<xml style="display: none">'; const xmlClose = '</xml>'; /** - * @param {!boolean} isStage - Whether the toolbox is for a stage-type target. + * @param {!boolean} isInitialSetup - Whether the toolbox is for initial setup. If the mode is "initial setup", + * blocks with localized default parameters (e.g. ask and wait) should not be loaded. (LLK/scratch-gui#5445) + * @param {?boolean} isStage - Whether the toolbox is for a stage-type target. This is always set to true + * when isInitialSetup is true. * @param {?string} targetId - The current editing target * @param {?Array.<object>} categoriesXML - optional array of `{id,xml}` for categories. This can include both core * and other extensions: core extensions will be placed in the normal Scratch order; others will go at the bottom. @@ -725,8 +732,9 @@ const xmlClose = '</xml>'; * @param {?string} soundName - The name of the default selected sound dropdown. * @returns {string} - a ScratchBlocks-style XML document for the contents of the toolbox. */ -const makeToolboxXML = function (isStage, targetId, categoriesXML = [], +const makeToolboxXML = function (isInitialSetup, isStage = true, targetId, categoriesXML = [], costumeName = '', backdropName = '', soundName = '') { + isStage = isInitialSetup || isStage; const gap = [categorySeparator]; costumeName = xmlEscape(costumeName); @@ -743,15 +751,15 @@ const makeToolboxXML = function (isStage, targetId, categoriesXML = [], } // return `undefined` }; - const motionXML = moveCategory('motion') || motion(isStage, targetId); - const looksXML = moveCategory('looks') || looks(isStage, targetId, costumeName, backdropName); - const soundXML = moveCategory('sound') || sound(isStage, targetId, soundName); - const eventsXML = moveCategory('event') || events(isStage, targetId); - const controlXML = moveCategory('control') || control(isStage, targetId); - const sensingXML = moveCategory('sensing') || sensing(isStage, targetId); - const operatorsXML = moveCategory('operators') || operators(isStage, targetId); - const variablesXML = moveCategory('data') || variables(isStage, targetId); - const myBlocksXML = moveCategory('procedures') || myBlocks(isStage, targetId); + const motionXML = moveCategory('motion') || motion(isInitialSetup, isStage, targetId); + const looksXML = moveCategory('looks') || looks(isInitialSetup, isStage, targetId, costumeName, backdropName); + const soundXML = moveCategory('sound') || sound(isInitialSetup, isStage, targetId, soundName); + const eventsXML = moveCategory('event') || events(isInitialSetup, isStage, targetId); + const controlXML = moveCategory('control') || control(isInitialSetup, isStage, targetId); + const sensingXML = moveCategory('sensing') || sensing(isInitialSetup, isStage, targetId); + const operatorsXML = moveCategory('operators') || operators(isInitialSetup, isStage, targetId); + const variablesXML = moveCategory('data') || variables(isInitialSetup, isStage, targetId); + const myBlocksXML = moveCategory('procedures') || myBlocks(isInitialSetup, isStage, targetId); const everything = [ xmlOpen, diff --git a/test/integration/localization.test.js b/test/integration/localization.test.js index 5c1403658eb75f9651eccdea91787a528e530818..225aa49b82f4068278521560768dbd8d5a1a8bbe 100644 --- a/test/integration/localization.test.js +++ b/test/integration/localization.test.js @@ -8,7 +8,8 @@ const { getLogs, loadUri, scope, - rightClickText + rightClickText, + findByText } = new SeleniumHelper(); const uri = path.resolve(__dirname, '../../build/index.html'); @@ -66,4 +67,15 @@ describe('Localization', () => { const logs = await getLogs(); await expect(logs).toEqual([]); }); + + // test for #5445 + test('Loading with locale shows correct translation for string length block parameter', async () => { + await loadUri(`${uri}?locale=ja`); + await clickText('演算'); // Operators category in Japanese + await new Promise(resolve => setTimeout(resolve, 1000)); // wait for blocks to scroll + await clickText('ã®é•·ã•', scope.blocksTab); // Click "length <apple>" block + await findByText('3', scope.reportedValue); // Tooltip with result + const logs = await getLogs(); + await expect(logs).toEqual([]); + }); });