Add localization support to music and video sensing extensions

This commit is contained in:
Andrew Sliwinski 2018-05-10 13:41:41 -04:00
parent 6ffc206d10
commit 1995753e94
3 changed files with 289 additions and 59 deletions

View file

@ -15,8 +15,8 @@
"build": "webpack --progress --colors --bail", "build": "webpack --progress --colors --bail",
"coverage": "tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov", "coverage": "tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov",
"deploy": "touch playground/.nojekyll && gh-pages -t -d playground -m \"Build for $(git log --pretty=format:%H -n1)\"", "deploy": "touch playground/.nojekyll && gh-pages -t -d playground -m \"Build for $(git log --pretty=format:%H -n1)\"",
"extract:pen": "mkdirp translations/pen && format-message extract --out-file translations/pen/en.json src/extensions/scratch3_pen/index.js", "extract:core": "mkdirp translations/core && format-message extract --out-file translations/core/en.json src/extensions/**/index.js",
"i18n:src": "npm run extract:pen", "i18n:src": "npm run extract:core",
"lint": "eslint . && format-message lint src/**/*.js", "lint": "eslint . && format-message lint src/**/*.js",
"prepublish": "in-publish && npm run build || not-in-publish", "prepublish": "in-publish && npm run build || not-in-publish",
"start": "webpack-dev-server", "start": "webpack-dev-server",

View file

@ -2,6 +2,7 @@ const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type'); const BlockType = require('../../extension-support/block-type');
const Clone = require('../../util/clone'); const Clone = require('../../util/clone');
const Cast = require('../../util/cast'); const Cast = require('../../util/cast');
const formatMessage = require('format-message');
const MathUtil = require('../../util/math-util'); const MathUtil = require('../../util/math-util');
const Timer = require('../../util/timer'); const Timer = require('../../util/timer');
@ -192,75 +193,147 @@ class Scratch3MusicBlocks {
get DRUM_INFO () { get DRUM_INFO () {
return [ return [
{ {
name: '(1) Snare Drum', name: formatMessage({
id: 'music.drumSnare',
default: '(1) Snare Drum',
description: 'Sound of snare drum as used in a standard drum kit'
}),
fileName: '1-snare' fileName: '1-snare'
}, },
{ {
name: '(2) Bass Drum', name: formatMessage({
id: 'music.drumBass',
default: '(2) Bass Drum',
description: 'Sound of bass drum as used in a standard drum kit'
}),
fileName: '2-bass-drum' fileName: '2-bass-drum'
}, },
{ {
name: '(3) Side Stick', name: formatMessage({
id: 'music.drumSideStick',
default: '(3) Side Stick',
description: 'Sound of a drum stick hitting the side of a drum (usually the snare)'
}),
fileName: '3-side-stick' fileName: '3-side-stick'
}, },
{ {
name: '(4) Crash Cymbal', name: formatMessage({
id: 'music.drumCrashCymbal',
default: '(4) Crash Cymbal',
description: 'Sound of a drum stick hitting a crash cymbal'
}),
fileName: '4-crash-cymbal' fileName: '4-crash-cymbal'
}, },
{ {
name: '(5) Open Hi-Hat', name: formatMessage({
id: 'music.drumOpenHiHat',
default: '(5) Open Hi-Hat',
description: 'Sound of a drum stick hitting a hi-hat while open'
}),
fileName: '5-open-hi-hat' fileName: '5-open-hi-hat'
}, },
{ {
name: '(6) Closed Hi-Hat', name: formatMessage({
id: 'music.drumClosedHiHat',
default: '(6) Closed Hi-Hat',
description: 'Sound of a drum stick hitting a hi-hat while closed'
}),
fileName: '6-closed-hi-hat' fileName: '6-closed-hi-hat'
}, },
{ {
name: '(7) Tambourine', name: formatMessage({
id: 'music.drumTambourine',
default: '(7) Tambourine',
description: 'Sound of a tambourine being struck'
}),
fileName: '7-tambourine' fileName: '7-tambourine'
}, },
{ {
name: '(8) Hand Clap', name: formatMessage({
id: 'music.drumHandClap',
default: '(8) Hand Clap',
description: 'Sound of two hands clapping together'
}),
fileName: '8-hand-clap' fileName: '8-hand-clap'
}, },
{ {
name: '(9) Claves', name: formatMessage({
id: 'music.drumClaves',
default: '(9) Claves',
description: 'Sound of a claves being struck together'
}),
fileName: '9-claves' fileName: '9-claves'
}, },
{ {
name: '(10) Wood Block', name: formatMessage({
id: 'music.drumWoodBlock',
default: '(10) Wood Block',
description: 'Sound of a wood block being struck'
}),
fileName: '10-wood-block' fileName: '10-wood-block'
}, },
{ {
name: '(11) Cowbell', name: formatMessage({
id: 'music.drumCowbell',
default: '(11) Cowbell',
description: 'Sound of a cowbell being struck'
}),
fileName: '11-cowbell' fileName: '11-cowbell'
}, },
{ {
name: '(12) Triangle', name: formatMessage({
id: 'music.drumTriangle',
default: '(12) Triangle',
description: 'Sound of a triangle (instrument) being struck'
}),
fileName: '12-triangle' fileName: '12-triangle'
}, },
{ {
name: '(13) Bongo', name: formatMessage({
id: 'music.drumBongo',
default: '(13) Bongo',
description: 'Sound of a bongo being struck'
}),
fileName: '13-bongo' fileName: '13-bongo'
}, },
{ {
name: '(14) Conga', name: formatMessage({
id: 'music.drumConga',
default: '(14) Conga',
description: 'Sound of a conga being struck'
}),
fileName: '14-conga' fileName: '14-conga'
}, },
{ {
name: '(15) Cabasa', name: formatMessage({
id: 'music.drumCabasa',
default: '(15) Cabasa',
description: 'Sound of a cabasa being shaken'
}),
fileName: '15-cabasa' fileName: '15-cabasa'
}, },
{ {
name: '(16) Guiro', name: formatMessage({
id: 'music.drumGuiro',
default: '(16) Guiro',
description: 'Sound of a guiro being played'
}),
fileName: '16-guiro' fileName: '16-guiro'
}, },
{ {
name: '(17) Vibraslap', name: formatMessage({
id: 'music.drumVibraslap',
default: '(17) Vibraslap',
description: 'Sound of a Vibraslap being played'
}),
fileName: '17-vibraslap' fileName: '17-vibraslap'
}, },
{ {
name: '(18) Cuica', name: formatMessage({
id: 'music.drumCuica',
default: '(18) Cuica',
description: 'Sound of a cuica being played'
}),
fileName: '18-cuica' fileName: '18-cuica'
} }
]; ];
@ -278,120 +351,204 @@ class Scratch3MusicBlocks {
get INSTRUMENT_INFO () { get INSTRUMENT_INFO () {
return [ return [
{ {
name: '(1) Piano', name: formatMessage({
id: 'music.instrumentPiano',
default: '(1) Piano',
description: 'Sound of a piano'
}),
dirName: '1-piano', dirName: '1-piano',
releaseTime: 0.5, releaseTime: 0.5,
samples: [24, 36, 48, 60, 72, 84, 96, 108] samples: [24, 36, 48, 60, 72, 84, 96, 108]
}, },
{ {
name: '(2) Electric Piano', name: formatMessage({
id: 'music.instrumentElectricPiano',
default: '(2) Electric Piano',
description: 'Sound of an electric piano'
}),
dirName: '2-electric-piano', dirName: '2-electric-piano',
releaseTime: 0.5, releaseTime: 0.5,
samples: [60] samples: [60]
}, },
{ {
name: '(3) Organ', name: formatMessage({
id: 'music.instrumentOrgan',
default: '(3) Organ',
description: 'Sound of an organ'
}),
dirName: '3-organ', dirName: '3-organ',
releaseTime: 0.5, releaseTime: 0.5,
samples: [60] samples: [60]
}, },
{ {
name: '(4) Guitar', name: formatMessage({
id: 'music.instrumentGuitar',
default: '(4) Guitar',
description: 'Sound of an accoustic guitar'
}),
dirName: '4-guitar', dirName: '4-guitar',
releaseTime: 0.5, releaseTime: 0.5,
samples: [60] samples: [60]
}, },
{ {
name: '(5) Electric Guitar', name: formatMessage({
id: 'music.instrumentElectricGuitar',
default: '(5) Electric Guitar',
description: 'Sound of an electric guitar'
}),
dirName: '5-electric-guitar', dirName: '5-electric-guitar',
releaseTime: 0.5, releaseTime: 0.5,
samples: [60] samples: [60]
}, },
{ {
name: '(6) Bass', name: formatMessage({
id: 'music.instrumentBass',
default: '(6) Bass',
description: 'Sound of an accoustic upright bass'
}),
dirName: '6-bass', dirName: '6-bass',
releaseTime: 0.25, releaseTime: 0.25,
samples: [36, 48] samples: [36, 48]
}, },
{ {
name: '(7) Pizzicato', name: formatMessage({
id: 'music.instrumentPizzicato',
default: '(7) Pizzicato',
description: 'Sound of a string instrument (e.g. violin) being plucked'
}),
dirName: '7-pizzicato', dirName: '7-pizzicato',
releaseTime: 0.25, releaseTime: 0.25,
samples: [60] samples: [60]
}, },
{ {
name: '(8) Cello', name: formatMessage({
id: 'music.instrumentCello',
default: '(8) Cello',
description: 'Sound of a cello being played with a bow'
}),
dirName: '8-cello', dirName: '8-cello',
releaseTime: 0.1, releaseTime: 0.1,
samples: [36, 48, 60] samples: [36, 48, 60]
}, },
{ {
name: '(9) Trombone', name: formatMessage({
id: 'music.instrumentTrombone',
default: '(9) Trombone',
description: 'Sound of a trombone being played'
}),
dirName: '9-trombone', dirName: '9-trombone',
samples: [36, 48, 60] samples: [36, 48, 60]
}, },
{ {
name: '(10) Clarinet', name: formatMessage({
id: 'music.instrumentClarinet',
default: '(10) Clarinet',
description: 'Sound of a clarinet being played'
}),
dirName: '10-clarinet', dirName: '10-clarinet',
samples: [48, 60] samples: [48, 60]
}, },
{ {
name: '(11) Saxophone', name: formatMessage({
id: 'music.instrumentSaxophone',
default: '(11) Saxophone',
description: 'Sound of a saxophone being played'
}),
dirName: '11-saxophone', dirName: '11-saxophone',
samples: [36, 60, 84] samples: [36, 60, 84]
}, },
{ {
name: '(12) Flute', name: formatMessage({
id: 'music.instrumentFlute',
default: '(12) Flute',
description: 'Sound of a flute being played'
}),
dirName: '12-flute', dirName: '12-flute',
samples: [60, 72] samples: [60, 72]
}, },
{ {
name: '(13) Wooden Flute', name: formatMessage({
id: 'music.instrumentWoodenFlute',
default: '(13) Wooden Flute',
description: 'Sound of a wooden flute being played'
}),
dirName: '13-wooden-flute', dirName: '13-wooden-flute',
samples: [60, 72] samples: [60, 72]
}, },
{ {
name: '(14) Bassoon', name: formatMessage({
id: 'music.instrumentBassoon',
default: '(14) Bassoon',
description: 'Sound of a bassoon being played'
}),
dirName: '14-bassoon', dirName: '14-bassoon',
samples: [36, 48, 60] samples: [36, 48, 60]
}, },
{ {
name: '(15) Choir', name: formatMessage({
id: 'music.instrumentChoir',
default: '(15) Choir',
description: 'Sound of a choir singing'
}),
dirName: '15-choir', dirName: '15-choir',
releaseTime: 0.25, releaseTime: 0.25,
samples: [48, 60, 72] samples: [48, 60, 72]
}, },
{ {
name: '(16) Vibraphone', name: formatMessage({
id: 'music.instrumentVibraphone',
default: '(16) Vibraphone',
description: 'Sound of a vibraphone being struck'
}),
dirName: '16-vibraphone', dirName: '16-vibraphone',
releaseTime: 0.5, releaseTime: 0.5,
samples: [60, 72] samples: [60, 72]
}, },
{ {
name: '(17) Music Box', name: formatMessage({
id: 'music.instrumentMusicBox',
default: '(17) Music Box',
description: 'Sound of a music box playing'
}),
dirName: '17-music-box', dirName: '17-music-box',
releaseTime: 0.25, releaseTime: 0.25,
samples: [60] samples: [60]
}, },
{ {
name: '(18) Steel Drum', name: formatMessage({
id: 'music.instrumentSteelDrum',
default: '(18) Steel Drum',
description: 'Sound of a steel drum being struck'
}),
dirName: '18-steel-drum', dirName: '18-steel-drum',
releaseTime: 0.5, releaseTime: 0.5,
samples: [60] samples: [60]
}, },
{ {
name: '(19) Marimba', name: formatMessage({
id: 'music.instrumentMarimba',
default: '(19) Marimba',
description: 'Sound of a marimba being struck'
}),
dirName: '19-marimba', dirName: '19-marimba',
samples: [60] samples: [60]
}, },
{ {
name: '(20) Synth Lead', name: formatMessage({
id: 'music.instrumentSynthLead',
default: '(20) Synth Lead',
description: 'Sound of a "lead" synthesizer being played'
}),
dirName: '20-synth-lead', dirName: '20-synth-lead',
releaseTime: 0.1, releaseTime: 0.1,
samples: [60] samples: [60]
}, },
{ {
name: '(21) Synth Pad', name: formatMessage({
id: 'music.instrumentSynthPad',
default: '(21) Synth Pad',
description: 'Sound of a "pad" synthesizer being played'
}),
dirName: '21-synth-pad', dirName: '21-synth-pad',
releaseTime: 0.25, releaseTime: 0.25,
samples: [60] samples: [60]
@ -492,7 +649,11 @@ class Scratch3MusicBlocks {
{ {
opcode: 'playDrumForBeats', opcode: 'playDrumForBeats',
blockType: BlockType.COMMAND, blockType: BlockType.COMMAND,
text: 'play drum [DRUM] for [BEATS] beats', text: formatMessage({
id: 'music.playDrumForBeats',
default: 'play drum [DRUM] for [BEATS] beats',
description: 'play drum sample for a number of beats'
}),
arguments: { arguments: {
DRUM: { DRUM: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,
@ -508,7 +669,11 @@ class Scratch3MusicBlocks {
{ {
opcode: 'restForBeats', opcode: 'restForBeats',
blockType: BlockType.COMMAND, blockType: BlockType.COMMAND,
text: 'rest for [BEATS] beats', text: formatMessage({
id: 'music.restForBeats',
default: 'rest for [BEATS] beats',
description: 'rest (play no sound) for a number of beats'
}),
arguments: { arguments: {
BEATS: { BEATS: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,
@ -519,7 +684,11 @@ class Scratch3MusicBlocks {
{ {
opcode: 'playNoteForBeats', opcode: 'playNoteForBeats',
blockType: BlockType.COMMAND, blockType: BlockType.COMMAND,
text: 'play note [NOTE] for [BEATS] beats', text: formatMessage({
id: 'music.playNoteForBeats',
default: 'play note [NOTE] for [BEATS] beats',
description: 'play a note for a number of beats'
}),
arguments: { arguments: {
NOTE: { NOTE: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,
@ -534,7 +703,11 @@ class Scratch3MusicBlocks {
{ {
opcode: 'setInstrument', opcode: 'setInstrument',
blockType: BlockType.COMMAND, blockType: BlockType.COMMAND,
text: 'set instrument to [INSTRUMENT]', text: formatMessage({
id: 'music.setInstrument',
default: 'set instrument to [INSTRUMENT]',
description: 'set the instrument (e.g. piano, guitar, trombone) for notes played'
}),
arguments: { arguments: {
INSTRUMENT: { INSTRUMENT: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,
@ -546,7 +719,11 @@ class Scratch3MusicBlocks {
{ {
opcode: 'setTempo', opcode: 'setTempo',
blockType: BlockType.COMMAND, blockType: BlockType.COMMAND,
text: 'set tempo to [TEMPO]', text: formatMessage({
id: 'music.setTempo',
default: 'set tempo to [TEMPO]',
description: 'set tempo (speed) for notes, drums, and rests played'
}),
arguments: { arguments: {
TEMPO: { TEMPO: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,
@ -557,7 +734,11 @@ class Scratch3MusicBlocks {
{ {
opcode: 'changeTempo', opcode: 'changeTempo',
blockType: BlockType.COMMAND, blockType: BlockType.COMMAND,
text: 'change tempo by [TEMPO]', text: formatMessage({
id: 'music.changeTempo',
default: 'change tempo by [TEMPO]',
description: 'change tempo (speed) for notes, drums, and rests played'
}),
arguments: { arguments: {
TEMPO: { TEMPO: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,
@ -567,7 +748,11 @@ class Scratch3MusicBlocks {
}, },
{ {
opcode: 'getTempo', opcode: 'getTempo',
text: 'tempo', text: formatMessage({
id: 'music.getTempo',
default: 'tempo',
description: 'get the current tempo (speed) for notes, drums, and rests played'
}),
blockType: BlockType.REPORTER blockType: BlockType.REPORTER
} }
], ],

View file

@ -4,6 +4,7 @@ const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type'); const BlockType = require('../../extension-support/block-type');
const Clone = require('../../util/clone'); const Clone = require('../../util/clone');
const Cast = require('../../util/cast'); const Cast = require('../../util/cast');
const formatMessage = require('format-message');
const Video = require('../../io/video'); const Video = require('../../io/video');
const VideoMotion = require('./library'); const VideoMotion = require('./library');
@ -267,11 +268,19 @@ class Scratch3VideoSensingBlocks {
get ATTRIBUTE_INFO () { get ATTRIBUTE_INFO () {
return [ return [
{ {
name: 'motion', name: formatMessage({
id: 'videoSensing.motion',
default: 'motion',
description: 'Attribute for the "video [ATTRIBUTE] on [SUBJECT]" block'
}),
value: SensingAttribute.MOTION value: SensingAttribute.MOTION
}, },
{ {
name: 'direction', name: formatMessage({
id: 'videoSensing.direction',
default: 'direction',
description: 'Attribute for the "video [ATTRIBUTE] on [SUBJECT]" block'
}),
value: SensingAttribute.DIRECTION value: SensingAttribute.DIRECTION
} }
]; ];
@ -290,11 +299,19 @@ class Scratch3VideoSensingBlocks {
get SUBJECT_INFO () { get SUBJECT_INFO () {
return [ return [
{ {
name: 'sprite', name: formatMessage({
id: 'videoSensing.sprite',
default: 'sprite',
description: 'Subject for the "video [ATTRIBUTE] on [SUBJECT]" block'
}),
value: SensingSubject.SPRITE value: SensingSubject.SPRITE
}, },
{ {
name: 'stage', name: formatMessage({
id: 'videoSensing.stage',
default: 'stage',
description: 'Subject for the "video [ATTRIBUTE] on [SUBJECT]" block'
}),
value: SensingSubject.STAGE value: SensingSubject.STAGE
} }
]; ];
@ -318,15 +335,27 @@ class Scratch3VideoSensingBlocks {
get VIDEO_STATE_INFO () { get VIDEO_STATE_INFO () {
return [ return [
{ {
name: 'off', name: formatMessage({
id: 'videoSensing.off',
default: 'off',
description: 'Option for the "turn video [STATE]" block'
}),
value: VideoState.OFF value: VideoState.OFF
}, },
{ {
name: 'on', name: formatMessage({
id: 'videoSensing.on',
default: 'on',
description: 'Option for the "turn video [STATE]" block'
}),
value: VideoState.ON value: VideoState.ON
}, },
{ {
name: 'on flipped', name: formatMessage({
id: 'videoSensing.onFlipped',
default: 'on flipped',
description: 'Option for the "turn video [STATE]" block'
}),
value: VideoState.ON_FLIPPED value: VideoState.ON_FLIPPED
} }
]; ];
@ -348,7 +377,11 @@ class Scratch3VideoSensingBlocks {
// @todo this hat needs to be set itself to restart existing // @todo this hat needs to be set itself to restart existing
// threads like Scratch 2's behaviour. // threads like Scratch 2's behaviour.
opcode: 'whenMotionGreaterThan', opcode: 'whenMotionGreaterThan',
text: 'when video motion > [REFERENCE]', text: formatMessage({
id: 'videoSensing.whenMotionGreaterThan',
default: 'when video motion > [REFERENCE]',
description: 'Event that triggers when the amount of motion is greater than [REFERENCE]'
}),
blockType: BlockType.HAT, blockType: BlockType.HAT,
arguments: { arguments: {
REFERENCE: { REFERENCE: {
@ -360,7 +393,11 @@ class Scratch3VideoSensingBlocks {
{ {
opcode: 'videoOn', opcode: 'videoOn',
blockType: BlockType.REPORTER, blockType: BlockType.REPORTER,
text: 'video [ATTRIBUTE] on [SUBJECT]', text: formatMessage({
id: 'videoSensing.videoOn',
default: 'video [ATTRIBUTE] on [SUBJECT]',
description: 'Reporter that returns the amount of [ATTRIBUTE] for the selected [SUBJECT]'
}),
arguments: { arguments: {
ATTRIBUTE: { ATTRIBUTE: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,
@ -376,7 +413,11 @@ class Scratch3VideoSensingBlocks {
}, },
{ {
opcode: 'videoToggle', opcode: 'videoToggle',
text: 'turn video [VIDEO_STATE]', text: formatMessage({
id: 'videoSensing.videoToggle',
default: 'turn video [VIDEO_STATE]',
description: 'Controls dispay of the video preview layer'
}),
arguments: { arguments: {
VIDEO_STATE: { VIDEO_STATE: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,
@ -387,7 +428,11 @@ class Scratch3VideoSensingBlocks {
}, },
{ {
opcode: 'setVideoTransparency', opcode: 'setVideoTransparency',
text: 'set video transparency to [TRANSPARENCY]', text: formatMessage({
id: 'videoSensing.setVideoTransparency',
default: 'set video transparency to [TRANSPARENCY]',
description: 'Controls transparency of the video preview layer'
}),
arguments: { arguments: {
TRANSPARENCY: { TRANSPARENCY: {
type: ArgumentType.NUMBER, type: ArgumentType.NUMBER,