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