From 102d3d7e793ab9ffcc55cbc7dea01c9e7c8a507a Mon Sep 17 00:00:00 2001 From: chrisgarrity <chrisg@media.mit.edu> Date: Fri, 14 Sep 2018 13:40:41 -0400 Subject: [PATCH] Localize default project (temporary solution) --- src/lib/default-project/index.js | 69 +++++----- src/lib/default-project/project-data.js | 150 +++++++++++++++++++++ src/lib/project-loader-hoc.jsx | 5 +- src/lib/storage.js | 16 ++- test/unit/util/project-loader-hoc.test.jsx | 6 +- 5 files changed, 209 insertions(+), 37 deletions(-) create mode 100644 src/lib/default-project/project-data.js diff --git a/src/lib/default-project/index.js b/src/lib/default-project/index.js index 332e1bd98..df1154b6e 100644 --- a/src/lib/default-project/index.js +++ b/src/lib/default-project/index.js @@ -1,5 +1,5 @@ import {TextEncoder} from 'text-encoding'; -import projectJson from './project.json'; +import projectData from './project-data'; /* eslint-disable import/no-unresolved */ import popWav from '!arraybuffer-loader!./83a9787d4cb6f3b7632b4ddfebf74367.wav'; @@ -10,34 +10,39 @@ import costume2 from '!raw-loader!./3696356a03a8d938318876a593572843.svg'; /* eslint-enable import/no-unresolved */ const encoder = new TextEncoder(); -export default [{ - id: 0, - assetType: 'Project', - dataFormat: 'JSON', - data: JSON.stringify(projectJson) -}, { - id: '83a9787d4cb6f3b7632b4ddfebf74367', - assetType: 'Sound', - dataFormat: 'WAV', - data: new Uint8Array(popWav) -}, { - id: '83c36d806dc92327b9e7049a565c6bff', - assetType: 'Sound', - dataFormat: 'WAV', - data: new Uint8Array(meowWav) -}, { - id: 'cd21514d0531fdffb22204e0ec5ed84a', - assetType: 'ImageVector', - dataFormat: 'SVG', - data: encoder.encode(backdrop) -}, { - id: '09dc888b0b7df19f70d81588ae73420e', - assetType: 'ImageVector', - dataFormat: 'SVG', - data: encoder.encode(costume1) -}, { - id: '3696356a03a8d938318876a593572843', - assetType: 'ImageVector', - dataFormat: 'SVG', - data: encoder.encode(costume2) -}]; +const defaultProject = translator => { + const projectJson = projectData(translator); + return [{ + id: 0, + assetType: 'Project', + dataFormat: 'JSON', + data: JSON.stringify(projectJson) + }, { + id: '83a9787d4cb6f3b7632b4ddfebf74367', + assetType: 'Sound', + dataFormat: 'WAV', + data: new Uint8Array(popWav) + }, { + id: '83c36d806dc92327b9e7049a565c6bff', + assetType: 'Sound', + dataFormat: 'WAV', + data: new Uint8Array(meowWav) + }, { + id: 'cd21514d0531fdffb22204e0ec5ed84a', + assetType: 'ImageVector', + dataFormat: 'SVG', + data: encoder.encode(backdrop) + }, { + id: '09dc888b0b7df19f70d81588ae73420e', + assetType: 'ImageVector', + dataFormat: 'SVG', + data: encoder.encode(costume1) + }, { + id: '3696356a03a8d938318876a593572843', + assetType: 'ImageVector', + dataFormat: 'SVG', + data: encoder.encode(costume2) + }]; +}; + +export default defaultProject; diff --git a/src/lib/default-project/project-data.js b/src/lib/default-project/project-data.js new file mode 100644 index 000000000..c021303aa --- /dev/null +++ b/src/lib/default-project/project-data.js @@ -0,0 +1,150 @@ +import {defineMessages} from 'react-intl'; + +const messages = defineMessages({ + backdrop: { + defaultMessage: 'backdrop1', + description: 'Default name for the first backdrop', + id: 'gui.defaultProject.backdrop' + }, + pop: { + defaultMessage: 'pop', + description: 'Default name for the pop sound', + id: 'gui.defaultProject.pop' + }, + sprite: { + defaultMessage: 'Sprite1', + description: 'Name for the default sprite', + id: 'gui.defaultProject.sprite' + }, + costume1: { + defaultMessage: 'costume1', + description: 'Name for the first default costume', + id: 'gui.defaultProject.costume1' + }, + costume2: { + defaultMessage: 'costume2', + description: 'Name for the second default costume', + id: 'gui.defaultProject.costume2' + }, + meow: { + defaultMessage: 'Meow', + description: 'Name for the meow sound', + id: 'gui.defaultProject.meow' + }, + variable: { + defaultMessage: 'my variable', + description: 'Name for the default variable', + id: 'gui.defaultProject.variable' + } +}); + +// use the default message if a translation function is not passed +const defaultTranslator = msgObj => msgObj.defaultMessage; + +/** + * Generate a localized version of the default project + * @param {function} translateFunction a function to use for translating the default names + * @return {json} the project data json for the default project + */ +const projectData = translateFunction => { + const translator = translateFunction || defaultTranslator; + return ({ + targets: [ + { + isStage: true, + name: 'Stage', + variables: { + '`jEk@4|i[#Fk?(8x)AV.-my variable': [ + translator(messages.variable), + 0 + ] + }, + lists: {}, + broadcasts: {}, + blocks: {}, + currentCostume: 0, + costumes: [ + { + assetId: 'cd21514d0531fdffb22204e0ec5ed84a', + name: translator(messages.backdrop), + md5ext: 'cd21514d0531fdffb22204e0ec5ed84a.svg', + dataFormat: 'svg', + rotationCenterX: 240, + rotationCenterY: 180 + } + ], + sounds: [ + { + assetId: '83a9787d4cb6f3b7632b4ddfebf74367', + name: translator(messages.pop), + dataFormat: 'wav', + format: '', + rate: 11025, + sampleCount: 258, + md5ext: '83a9787d4cb6f3b7632b4ddfebf74367.wav' + } + ], + volume: 100, + tempo: 60, + videoTransparency: 50, + videoState: 'off' + }, + { + isStage: false, + name: translator(messages.sprite), + variables: {}, + lists: {}, + broadcasts: {}, + blocks: {}, + currentCostume: 0, + costumes: [ + { + assetId: '09dc888b0b7df19f70d81588ae73420e', + name: translator(messages.costume1), + bitmapResolution: 1, + md5ext: '09dc888b0b7df19f70d81588ae73420e.svg', + dataFormat: 'svg', + rotationCenterX: 47, + rotationCenterY: 55 + }, + { + assetId: '3696356a03a8d938318876a593572843', + name: translator(messages.costume2), + bitmapResolution: 1, + md5ext: '3696356a03a8d938318876a593572843.svg', + dataFormat: 'svg', + rotationCenterX: 47, + rotationCenterY: 55 + } + ], + sounds: [ + { + assetId: '83c36d806dc92327b9e7049a565c6bff', + name: translator(messages.meow), + dataFormat: 'wav', + format: '', + rate: 22050, + sampleCount: 18688, + md5ext: '83c36d806dc92327b9e7049a565c6bff.wav' + } + ], + volume: 100, + visible: true, + x: 0, + y: 0, + size: 100, + direction: 90, + draggable: false, + rotationStyle: 'all around' + } + ], + meta: { + semver: '3.0.0', + vm: '0.1.0', + agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' // eslint-disable-line max-len + } + }); +}; + + +export default projectData; diff --git a/src/lib/project-loader-hoc.jsx b/src/lib/project-loader-hoc.jsx index 571f220fd..c045dc0ee 100644 --- a/src/lib/project-loader-hoc.jsx +++ b/src/lib/project-loader-hoc.jsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import {connect} from 'react-redux'; +import {injectIntl, intlShape} from 'react-intl'; import {setProjectId} from '../reducers/project-id'; @@ -24,6 +25,7 @@ const ProjectLoaderHOC = function (WrappedComponent) { }; storage.setProjectHost(props.projectHost); storage.setAssetHost(props.assetHost); + storage.setTranslatorFunction(props.intl.formatMessage); props.setProjectId(props.projectId); if ( props.projectId !== '' && @@ -88,6 +90,7 @@ const ProjectLoaderHOC = function (WrappedComponent) { } ProjectLoaderComponent.propTypes = { assetHost: PropTypes.string, + intl: intlShape.isRequired, projectHost: PropTypes.string, projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), setProjectId: PropTypes.func @@ -104,7 +107,7 @@ const ProjectLoaderHOC = function (WrappedComponent) { setProjectId: id => dispatch(setProjectId(id)) }); - return connect(mapStateToProps, mapDispatchToProps)(ProjectLoaderComponent); + return injectIntl(connect(mapStateToProps, mapDispatchToProps)(ProjectLoaderComponent)); }; export { diff --git a/src/lib/storage.js b/src/lib/storage.js index 38ee7ede4..fd04ca14c 100644 --- a/src/lib/storage.js +++ b/src/lib/storage.js @@ -1,6 +1,6 @@ import ScratchStorage from 'scratch-storage'; -import defaultProjectAssets from './default-project'; +import defaultProject from './default-project'; /** * Wrapper for ScratchStorage which adds default web sources. @@ -9,6 +9,7 @@ import defaultProjectAssets from './default-project'; class Storage extends ScratchStorage { constructor () { super(); + const defaultProjectAssets = defaultProject(this.translator); defaultProjectAssets.forEach(asset => this.cache( this.AssetType[asset.assetType], this.DataFormat[asset.dataFormat], @@ -54,6 +55,19 @@ class Storage extends ScratchStorage { getAssetGetConfig (asset) { return `${this.assetHost}/internalapi/asset/${asset.assetId}.${asset.dataFormat}/get/`; } + setTranslatorFunction (translator) { + this.translator = translator; + this.cacheDefaultProject(); + } + cacheDefaultProject () { + const defaultProjectAssets = defaultProject(this.translator); + defaultProjectAssets.forEach(asset => this.cache( + this.AssetType[asset.assetType], + this.DataFormat[asset.dataFormat], + asset.data, + asset.id + )); + } } const storage = new Storage(); diff --git a/test/unit/util/project-loader-hoc.test.jsx b/test/unit/util/project-loader-hoc.test.jsx index 6c30453e8..a0a51cc66 100644 --- a/test/unit/util/project-loader-hoc.test.jsx +++ b/test/unit/util/project-loader-hoc.test.jsx @@ -2,7 +2,7 @@ import React from 'react'; import configureStore from 'redux-mock-store'; import ProjectLoaderHOC from '../../../src/lib/project-loader-hoc.jsx'; import storage from '../../../src/lib/storage'; -import {mount} from 'enzyme'; +import {mountWithIntl} from '../../helpers/intl-helpers.jsx'; jest.mock('react-ga'); @@ -19,7 +19,7 @@ describe('ProjectLoaderHOC', () => { const WrappedComponent = ProjectLoaderHOC(Component); const originalLoad = storage.load; storage.load = jest.fn((type, id) => Promise.resolve({data: id})); - const mounted = mount( + const mounted = mountWithIntl( <WrappedComponent projectId="100" store={store} @@ -37,7 +37,7 @@ describe('ProjectLoaderHOC', () => { const WrappedComponent = ProjectLoaderHOC(Component); const originalLoad = storage.load; storage.load = jest.fn(() => Promise.resolve(null)); - const mounted = mount(<WrappedComponent store={store} />); + const mounted = mountWithIntl(<WrappedComponent store={store} />); storage.load = originalLoad; const mountedDiv = mounted.find('div'); expect(mountedDiv.exists()).toEqual(false); -- GitLab