Merge pull request #339 from LLK/release/2.2.5

Release 2.2.5
This commit is contained in:
Ray Schamp 2016-01-28 15:59:52 -05:00
commit 96a8775482
48 changed files with 668 additions and 449 deletions

1
.gitignore vendored
View file

@ -10,6 +10,7 @@ npm-*
# Locales
/locales
/intl
# Elastic Beanstalk Files
.elasticbeanstalk/*

View file

@ -32,7 +32,10 @@ deploy:
env: scratch-www-staging
on:
repo: LLK/scratch-www
branch: develop
branch:
- develop
- hotfix/*
- release/*
- provider: elasticbeanstalk
access_key_id: $EB_AWS_ACCESS_KEY_ID
secret_access_key: $EB_AWS_SECRET_ACCESS_KEY

35
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,35 @@
### Where am I? ###
Physically? No idea.
Digitally? Youre at Scratchs open source Web Client!
At Scratch, were working to update our UI to use a new codebase, which will be contained in this repository. The transition from existing to new codebase is an ongoing process, and we love to have people in the Scratch and Open Source communities help us along the way, and even afterwards as we develop new features for Scratch here.
### Who and what will I find here? ###
We are always excited to have people join us in working to make Scratch a wonderful place for people of all ages to make projects together. If youre new here, and looking to jump into our wonderful community, we have some wonderful resources for you to take a look at:
* [README](https://github.com/LLK/scratch-www/blob/develop/README.md) (if youre to read only one me in this repo, make it this one it has all of the necessary information for getting a local Scratch UI running on your machine!)
* [Community Guidelines](https://github.com/LLK/scratch-www/wiki/Community-Guidelines) (we find it important to maintain a constructive and welcoming community, just like on Scratch)
* [Issues](https://github.com/LLK/scratch-www/issues) where we keep track of all the things that need fixin on the website
Road map
Beyond this repo, there are also some other resources that you might want to take a look at:
[Scratch](https://scratch.mit.edu/) (the thing we work on)
[Open Source forum](https://scratch.mit.edu/discuss/49/) on Scratch (talk about the thing we work on on the thing we work on. so meta.)
[Bugs & Glitches forum](https://scratch.mit.edu/discuss/3/) on Scratch (where mosquitoes and dei ex machina congregate)
[Advanced Topics forum](https://scratch.mit.edu/discuss/31/) on Scratch (like Topics, but more complex-y)
### I wanna contribute! ###
Sweet! Here are some ways you can contribute:
* [Report bugs](https://github.com/LLK/scratch-www/wiki/Reporting-Bugs)
* [Work on bugs](https://github.com/LLK/scratch-www/wiki/Workflow-for-Repo-Contributions)
* Make sure to check out how to [assign yourself bugs](https://github.com/LLK/scratch-www/wiki/Assigning-Yourself-Bugs) too.
Were currently building Scratch using [React](https://facebook.github.io/react/) and [SCSS](http://sass-lang.com/documentation/file.SASS_REFERENCE.html). Here are some resources to help you get acquainted with how were working on the Scratch codebase:
* [Style Guide](https://github.com/LLK/scratch-www/wiki/Style-Guide)
* [Testing Guide](https://github.com/LLK/scratch-www/wiki/Testing-Guide)
* [Localization Guide](https://github.com/LLK/scratch-www/wiki/Localization-Guide)
* [Map of the repository](https://github.com/LLK/scratch-www/wiki/Repo-Map)

13
LICENSE.md Normal file
View file

@ -0,0 +1,13 @@
Copyright 2015 Massachusetts Institute of Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -18,8 +18,9 @@ build:
clean:
rm -rf ./build
rm -rf ./intl
mkdir -p build
mkdir -p locales
mkdir -p intl
deploy:
@ -38,7 +39,7 @@ tag:
echo $(GIT_VERSION) > ./build/version.txt
translations:
./lib/bin/build-locales locales/translations.json
./bin/build-locales intl
webpack:
$(WEBPACK) --bail
@ -63,7 +64,6 @@ lint:
$(ESLINT) ./*.js
$(ESLINT) ./server/*.js
$(ESLINT) ./src/*.js
$(ESLINT) ./src/*.jsx
$(ESLINT) ./src/mixins/*.jsx
$(ESLINT) ./src/views/**/*.jsx
$(ESLINT) ./src/components/**/*.jsx

View file

@ -3,6 +3,9 @@
[![Build Status](https://magnum.travis-ci.com/LLK/scratch-www.svg?token=xzzHj4ct3SyBTpeqxnx1)](https://magnum.travis-ci.com/LLK/scratch-www)
### Before Getting Started
* make sure you have node and npm [installed](https://docs.npmjs.com/getting-started/installing-node)
### To Build
```bash
npm install
@ -16,6 +19,10 @@ npm start
During development, `npm start` watches any update you make to files in either `./static` or `./src` and triggers a rebuild of the project. In development the build is stored in memory, and not served from the `./build` directory.
When running `npm start`, here are some important log messages to keep an eye out for:
* `webpack: bundle is now VALID.` the bundle has been loaded into memory and is now viewable in the browser. This will show up both once `npm start` has completed its setup, and also once updates you make to files have been re-compiled for viewing in the browser.
* `webpack: bundle is now INVALID.` if you see this, then it means you have made updates to files that are still being compiled for browser viewing. Pages will still be viewable, but they will not see any updates you made yet.
Once running, open `http://localhost:8333` in your browser. If you wish to have the server reload automatically, you can install either [nodemon](https://github.com/remy/nodemon) or [forever](https://github.com/foreverjs/forever).
### To stop
@ -30,7 +37,9 @@ Use `^C` to stop the node process `npm start` starts.
| `API_HOST` | `https://api.scratch.mit.edu` | Hostname for API requests |
| `NODE_ENV` | `null` | If not `production`, app acts like development |
| `PORT` | `8333` | Port for devserver (http://localhost:XXXX) |
| `FALLBACK` | `''` | Pass-through location for scratchr2 |
| `FALLBACK` | `''` | Pass-through location for old site |
**NOTE:** Because by default `API_HOST=https://api.scratch.mit.edu`, please be aware that, by default, you will be seeing and interacting with real data on the Scratch website.
### To Test
```bash
@ -47,3 +56,5 @@ Most of the issues we have currently revolve around the use of `FALLBACK`. This
Setting `FALLBACK=https://scratch.mit.edu` allows the web client to retrieve data from the Scratch website in your development environment. However, because of security concerns, trying to send data to Scratch through your development environment won't work. This means the following things will be broken for the time being:
* Login on the splash page (*In the process of being fixed*)
* Some update attempts to production data made through a development version of the web client
Additionally, if you set `FALLBACK=https://scratch.mit.edu`, be aware that clicking on links to parts of the website not yet migrated over (currently such as `Explore`, `Discuss`, `Profile`, etc.) will take you to the Scratch website itself.

170
bin/build-locales Executable file
View file

@ -0,0 +1,170 @@
#!/usr/bin/env node
/*
Converts the existing .po translation files in the module to JSON files.
Requires po2json in order to work. Takes as input a directory
in which to store the resulting json translation files.
Takes in as an argument an output directory to put translation files.
Searches for files named `l10n.json` in the `src/views/` directory to get
template english strings (as well as the general template at `src/l10n.json`).
It compiles the template strings into a flat object that is compared against the
translations in the .po files from the `scratchr2_translations` dependency, using
an md5 of the template string without whitespace, and an md5 of the .po msgid string
without whitespace.
The output files are javascript files that declare objects by locale. Each locale
has a sub-object with FormattedMessage ids as keys, and translated strings as
values. If no translation was found for a string, the default english will be the
value.
Output Example:
'''
var message = {
en: {
'general.inAWorld': 'In a world, where bears are invisible...',
'general.question': 'Are there bears here?',
'general.answer': 'I dunno, but there could be...'
},
es: {
'general.inAWorld': 'En un mundo, donde hay osos invisibles',
'general.question': 'Are there bears here?',
'general.answer': 'No sé, pero es posible...'
}
}
'''
*/
var fs = require('fs');
var glob = require('glob');
var merge = require('lodash.merge');
var path = require('path');
var po2icu = require('po2icu');
var localeCompare = require('./lib/locale-compare');
// -----------------------------------------------------------------------------
// Main script
// -----------------------------------------------------------------------------
var args = process.argv.slice(2);
if (!args.length) {
process.stdout.write('A destination directory must be specified.');
process.exit(1);
}
var verbose = false;
if (args.length > 1) {
verbose = (args[1] === '-v') ? true : false;
}
var poUiDir = path.resolve(__dirname, '../node_modules/scratchr2_translations/ui');
var outputDir = path.resolve(__dirname, '../', args[0]);
try {
fs.accessSync(outputDir, fs.F_OK);
} catch (err) {
// Doesn't exist - create it.
fs.mkdirSync(outputDir);
}
// get global locale strings first.
var globalTemplateFile = path.resolve(__dirname, '../src/l10n.json');
// message key with english string values (i.e. default values)
var generalIds = JSON.parse(fs.readFileSync(globalTemplateFile, 'utf8'));
var viewLocales = {};
var generalLocales = {
en: generalIds
};
// FormattedMessage id with english string as value. Use for default values in translations
// Sample structure: { 'general-general.blah': 'blah', 'about-about.blah': 'blahblah' }
var idsWithICU = {};
// reverse (i.e. english string with message key as the value) object for searching po files.
// Sample structure: { 'blah': 'general-general.blah', 'blahblah': 'about-about.blah' }
var icuWithIds = {};
for (var id in generalIds) {
idsWithICU['general-' + id] = generalIds[id];
icuWithIds[generalIds[id]] = 'general-' + id;
}
// start with all views, and remove localized ones as they are iterated over
var views = glob.sync(path.resolve(__dirname, '../src/views/*'));
for (var i = 0; i < views.length; i++) {
views[i] = views[i].split('/').pop();
}
// get view-specific locale strings.
var files = glob.sync(path.resolve(__dirname, '../src/views/**/l10n.json'));
files.forEach(function (file) {
var dirPath = file.split('/');
dirPath.pop();
var view = dirPath.pop();
var viewIds = JSON.parse(fs.readFileSync(file, 'utf8'));
viewLocales[view] = {
en: viewIds
};
for (var id in viewIds) {
idsWithICU[view + '-' + id] = viewIds[id];
icuWithIds[viewIds[id]] = view + '-' + id; // add viewName to identifier for later
}
});
// md5 of english strings with message key as the value for searching po files.
// Sample structure: { 'sdfas43534sdfasdf': 'general-general.blah', 'lkjfasdf4t342asdfa': 'about-about.blah' }
var md5WithIds = localeCompare.getMD5Map(icuWithIds);
// Get ui localization strings first
glob(poUiDir + '/*', function (err, files) {
if (err) throw new Error(err);
files.forEach(function (file) {
var lang = file.split('/').pop();
var jsFile = path.resolve(file, 'LC_MESSAGES/djangojs.po');
var pyFile = path.resolve(file, 'LC_MESSAGES/django.po');
var translations = {};
try {
var jsTranslations = po2icu.poFileToICUSync(lang, jsFile);
translations = localeCompare.mergeNewTranslations(translations, jsTranslations, idsWithICU, md5WithIds);
} catch (err) {
if (verbose) process.stdout.write(lang + ': ' + err + '\n');
}
try {
var pyTranslations = po2icu.poFileToICUSync(lang, pyFile);
translations = localeCompare.mergeNewTranslations(translations, pyTranslations, idsWithICU, md5WithIds);
} catch (err) {
if (verbose) process.stdout.write(lang + ': ' + err + '\n');
}
// add new translations to locale object
for (var id in translations) {
var ids = id.split('-'); // [viewName, stringId]
var viewName = ids[0];
var stringId = ids[1];
if (viewLocales.hasOwnProperty(viewName)) {
if (!viewLocales[viewName].hasOwnProperty(lang)) viewLocales[viewName][lang] = {};
viewLocales[viewName][lang][stringId] = translations[id];
} else {
// default to general
if (!generalLocales.hasOwnProperty(lang)) generalLocales[lang] = {};
generalLocales[lang][stringId] = translations[id];
}
}
});
for (var i in views) {
var viewTranslations = generalLocales;
if (views[i] in viewLocales) {
viewTranslations = merge(viewLocales[views[i]], viewTranslations);
}
var objectString = JSON.stringify(viewTranslations);
var fileString = 'window._messages = ' + objectString + ';';
fs.writeFileSync(outputDir + '/' + views[i] + '.intl.js', fileString);
}
});

133
en.json
View file

@ -1,133 +0,0 @@
{
"general.accountSettings": "Account settings",
"general.about": "About",
"general.aboutScratch": "About Scratch",
"general.donate": "Donate",
"general.collaborators": "Collaborators",
"general.community": "Community",
"general.contactUs": "Contact Us",
"general.copyright": "Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab",
"general.create": "Create",
"general.credits": "Credits",
"general.discuss": "Discuss",
"general.dmca": "DMCA",
"general.explore": "Explore",
"general.faq": "FAQ",
"general.forParents": "For Parents",
"general.forEducators": "For Educators",
"general.guidelines": "Community Guidelines",
"general.help": "Help",
"general.jobs": "Jobs",
"general.joinScratch": "Join Scratch",
"general.legal": "Legal",
"general.learnMore": "Learn More",
"general.messages": "Messages",
"general.myClasses": "My Classes",
"general.myStuff": "My Stuff",
"general.offlineEditor": "Offline Editor",
"general.profile": "Profile",
"general.scratchConference": "Scratch Conference",
"general.scratchday": "Scratch Day",
"general.scratchEd": "ScratchEd",
"general.scratchFoundation": "Scratch Foundation",
"general.scratchJr": "ScratchJr",
"general.search": "Search",
"general.signIn": "Sign in",
"general.statistics": "Statistics",
"general.support": "Support",
"general.tipsWindow": "Tips Window",
"general.tipsAnimateYourNameTitle": "Animate Your Name",
"general.tipsBearstack": "Bearstack Story",
"general.tipsDanceTitle": "Dance, Dance, Dance",
"general.tipsGetStarted": "Getting Started",
"general.tipsHideAndSeekTitle": "Hide-and-Seek Game",
"general.tipsPongGame": "Create a Pong Game",
"general.termsOfUse": "Terms of Use",
"general.username": "Username",
"general.viewAll": "View All",
"general.whatsHappening": "What's Happening?",
"general.wiki": "Scratch Wiki",
"about.introOne": "With Scratch, you can program your own interactive stories, games, and animations — and share your creations with others in the online community.",
"about.introTwo": "Scratch helps young people learn to think creatively, reason systematically, and work collaboratively — essential skills for life in the 21st century.",
"about.introThree": "Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab. It is provided free of charge.",
"about.introParents": "Info for parents",
"about.introEducators": "Info for educators",
"about.whoUsesScratch": "Who Uses Scratch?",
"about.whoUsesScratchDescription": "Scratch is designed especially for ages 8 to 16, but is used by people of all ages. Millions of people are creating Scratch projects in a wide variety of settings, including homes, schools, museums, libraries, and community centers.",
"about.aroundTheWorld": "Around the World",
"about.aroundTheWorldDescription": "Scratch is used in more than 150 different countries and available in more than 40 languages. To change languages, click the menu at the bottom of the page. Or, in the Project Editor, click the globe at the top of the page. To add or improve a translation, see the <a href=\"http://wiki.scratch.mit.edu/wiki/How_to_Translate_Scratch\">translation</a> page.",
"about.quotes": "Quotes",
"about.quotesDescription": "The Scratch Team has received many emails from youth, parents, and educators expressing thanks for Scratch. Want to see what people are saying? You can read a collection of the <a href=\"/info/quotes\">quotes</a> we&#39;ve received.",
"about.learnMore": "Learn More About Scratch",
"about.learnMoreHelp": "Scratch Help Page",
"about.learnMoreFaq": "Frequently Asked Questions",
"about.learnMoreParents": "Information for Parents",
"about.learnMoreCredits": "Scratch Credits",
"about.literacy": "Learn to Code, Code to Learn",
"about.literacyDescription": "The ability to code computer programs is an important part of literacy in todays society. When people learn to code in Scratch, they learn important strategies for solving problems, designing projects, and communicating ideas.",
"about.schools": "Scratch in Schools",
"about.schoolsDescription": "Students are learning with Scratch at all levels (from elementary school to college) and across disciplines (such as math, computer science, language arts, social studies). Educators share stories, exchange resources, ask questions, and find people on the <a href=\"http://scratched.gse.harvard.edu/\">ScratchEd website</a>.",
"about.research": "Research",
"about.researchDescription": "The MIT Scratch Team and collaborators are researching how people use and learn with Scratch (for an introduction, see <a href=\"http://web.media.mit.edu/~mres/papers/Scratch-CACM-final.pdf\">Scratch: Programming for All</a>). Find out more about Scratch <a href=\"/info/research\">research</a> and <a href=\"/statistics\">statistics</a> about Scratch.",
"about.support": "Support and Funding",
"about.supportDescription": "The Scratch project, initiated in 2003, has received generous support from the National Science Foundation (grants 0325828, 1002713, 1027848, 1019396), Intel Foundation, Microsoft, MacArthur Foundation, LEGO Foundation, Code-to-Learn Foundation, Google, Dell, Fastly, Inversoft, and MIT Media Lab research consortia. If you&#39;d like to support Scratch, please see our <a href=\"https://secure.donationpay.org/scratchfoundation/\">donate page</a>, or contact us at donate@scratch.mit.edu.",
"footer.about": "About Scratch",
"footer.discuss": "Discussion Forums",
"footer.help": "Help Page",
"footer.scratchFamily": "Scratch Family",
"hoc.activityCards": "Activity Cards",
"hoc.activityCardsHeader": "Activity Cards and Guides",
"hoc.activityCardsInfo1": "Want tips and ideas for these Hour of Code&trade; activities? Use the activity cards to get ideas for creating with Scratch. Facilitator guides can help you plan a group activity.",
"hoc.addToStudios": "Add Your Projects to Studios",
"hoc.addToStudiosDescription": "These studios include projects created by young people around the world. Take a look at the studios to get inspired - or submit your own projects to the studios!",
"hoc.facilitatorGuide": "Facilitator Guide",
"hoc.findOutMore": "Find out more",
"hoc.helpScratch": "Help with Scratch",
"hoc.helpScratchDescription": "You can find tutorials and helpful hints in the <a href=\"/projects/editor/?tip_bar=home\">Tips Window</a>. For more resources, see <a href=\"/help\">Scratch Help</a>",
"hoc.moreActivities": "Want More Activities?",
"hoc.moreDescription": "Check out these other tutorials. Or remix one of our <a href=\"/starter_projects\">Starter Projects</a>",
"hoc.officialNotice": "The \"Hour of Code&trade;\" is a nationwide initiative by <a href=\"http://csedweek.org\">Computer Science Education Week</a> and <a href=\"http://code.org\">Code.org</a> to introduce millions of students to one hour of computer science and computer programming.",
"hoc.studioAlice": "Alice in Wonderland Studio",
"hoc.studioWeBareBears": "We Bare Bears Studio",
"hoc.subTitle": "With Scratch, you can program your own stories, games, and animations — and share them online.",
"hoc.tipsDescription": "Need help getting started? Looking for ideas?&nbsp; You can find tutorials and helpful hints in the <a href=\"/projects/editor/?tip_bar=home\">Tips Window</a>",
"hoc.title": "Get Creative with Coding",
"intro.aboutScratch": "ABOUT SCRATCH",
"intro.forEducators": "FOR EDUCATORS",
"intro.forParents": "FOR PARENTS",
"intro.joinScratch": "JOIN SCRATCH",
"intro.seeExamples": "SEE EXAMPLES",
"intro.tagLine": "Create stories, games, and animations<br /> Share with others around the world",
"intro.tryItOut": "TRY IT OUT",
"login.forgotPassword": "Forgot Password?",
"navigation.signOut": "Sign out",
"news.scratchNews": "Scratch News",
"parents.FaqAgeRangeA": "While Scratch is primarily designed for 8 to 16 year olds, it is also used by people of all ages, including younger children with their parents.",
"parents.FaqAgeRangeQ": "What is the age range for Scratch?",
"parents.FaqResourcesQ": "What resources are available for learning Scratch?",
"parents.introDescription": "Scratch is a programming language and an online community where children can program and share interactive media such as stories, games, and animation with people from all over the world. As children create with Scratch, they learn to think creatively, work collaboratively, and reason systematically. Scratch is designed and maintained by the Lifelong Kindergarten group at the MIT Media Lab.",
"splash.featuredProjects": "Featured Projects",
"splash.featuredStudios": "Featured Studios",
"splash.projectsCuratedBy": "Projects Curated by",
"splash.scratchDesignStudioTitle": "Scratch Design Studio",
"splash.visitTheStudio": "Visit the studio",
"splash.recentlySharedProjects": "Recently Shared Projects",
"splash.projectsByScratchersFollowing": "Projects by Scratchers I'm Following",
"splash.projectsLovedByScratchersFollowing": "Projects Loved by Scratchers I'm Following",
"splash.projectsInStudiosFollowing": "Projects in Studios I'm Following",
"splash.communityRemixing": "What the Community is Remixing",
"splash.communityLoving": "What the Community is Loving",
"welcome.welcomeToScratch": "Welcome to Scratch!",
"welcome.learn": "Learn how to make a project in Scratch",
"welcome.tryOut": "Try out starter projects",
"welcome.connect": "Connect with other Scratchers"
}

View file

@ -1,78 +0,0 @@
#!/usr/bin/env node
/*
Converts the existing .po translation files in the module to JSON files.
Requires po2json in order to work. Takes as input a directory
in which to store the resulting json translation files.
*/
var fs = require('fs');
var glob = require('glob');
var path = require('path');
var po2icu = require('po2icu');
var localeCompare = require('../locale-compare');
// -----------------------------------------------------------------------------
// Main script
// -----------------------------------------------------------------------------
var args = process.argv.slice(2);
if (!args.length) {
process.stdout.write('A destination directory must be specified.');
process.exit(1);
}
var poUiDir = path.resolve(__dirname, '../../node_modules/scratchr2_translations/ui');
var outputFile = path.resolve(__dirname, '../../', args[0]);
// Create the directory if it doesn't exist.
var fileInfo = path.parse(outputFile);
try {
fs.accessSync(fileInfo.dir, fs.F_OK);
} catch (err) {
// Doesn't exist create it.
fs.mkdirSync(fileInfo.dir);
}
var icuTemplateFile = path.resolve(__dirname, '../../en.json');
var idsWithICU = JSON.parse(fs.readFileSync(icuTemplateFile, 'utf8'));
var locales = {
en: idsWithICU
};
var icuWithIds = {};
for (var id in idsWithICU) {
icuWithIds[idsWithICU[id]] = id;
}
var md5WithIds = localeCompare.getMD5Map(icuWithIds);
// Get ui localization strings first
glob(poUiDir + '/*', function (err, files) {
if (err) throw new Error(err);
files.forEach(function (file) {
var lang = file.split('/').pop();
var jsFile = path.resolve(file, 'LC_MESSAGES/djangojs.po');
var pyFile = path.resolve(file, 'LC_MESSAGES/django.po');
var translations = {};
try {
var jsTranslations = po2icu.poFileToICUSync(lang, jsFile);
translations = localeCompare.mergeNewTranslations(translations, jsTranslations, idsWithICU, md5WithIds);
} catch (err) {
process.stdout.write(lang + ': ' + err + '\n');
}
try {
var pyTranslations = po2icu.poFileToICUSync(lang, pyFile);
translations = localeCompare.mergeNewTranslations(translations, pyTranslations, idsWithICU, md5WithIds);
} catch (err) {
process.stdout.write(lang + ': ' + err + '\n');
}
locales[lang] = translations;
});
fs.writeFileSync(outputFile, JSON.stringify(locales, null, 4));
});

View file

@ -46,6 +46,7 @@
"jsx-loader": "0.13.2",
"lodash.clone": "3.0.3",
"lodash.defaultsdeep": "3.10.0",
"lodash.merge": "3.3.2",
"lodash.omit": "3.1.0",
"lodash.range": "3.0.1",
"minilog": "2.0.8",

View file

@ -23,7 +23,7 @@ function Handler (route) {
res.set({
'Content-Type': 'text/html',
'Cache-Control': 'public, max-age=31536000',
'Etag': 'W/"' + checksum + '"'
'Etag': '"' + checksum + '"'
});
res.send(output);
};

View file

@ -37,7 +37,7 @@ app.use(log());
app.use(compression());
if (isProduction) {
app.use(express.static(path.resolve(__dirname, '../build'), {
lastModified: true,
etag: 'strong',
maxAge: '1y'
}));
}

View file

@ -51,7 +51,7 @@
<script src="/js/lib/react-intl-with-locales{{min}}.js"></script>
<script src="/js/lib/raven.min.js"></script>
<script src="/js/main.bundle.js"></script>
<script src="/js/{{view}}.intl.js"></script>
<script src="/js/{{view}}.bundle.js"></script>
<!-- Error logging (Sentry) -->

View file

@ -46,7 +46,7 @@ var Activity = React.createClass({
return (
<li key={item.pk}>
<a href={actorProfileUrl}>
<img src={item.actor.thumbnail_url} width="34" height="34" />
<img src={item.actor.thumbnail_url} width="34" height="34" alt="" />
<p dangerouslySetInnerHTML={{__html: activityMessageHTML}}></p>
<p>
<span className="stamp">

View file

@ -3,6 +3,10 @@ var React = require('react');
require('./banner.scss');
/**
* Container for messages displayed below the nav bar that can be dismissed
* (See: email not confirmed banner)
*/
var Banner = React.createClass({
type: 'Banner',
propTypes: {

View file

@ -9,6 +9,9 @@ require('slick-carousel/slick/slick.scss');
require('slick-carousel/slick/slick-theme.scss');
require('./carousel.scss');
/**
* Displays content in horizontal scrolling box. Example usage: splash page rows.
*/
var Carousel = React.createClass({
type: 'Carousel',
propTypes: {

View file

@ -1,8 +1,5 @@
var omit = require('lodash.omit');
var React = require('react');
var ReactIntl = require('react-intl');
var FormattedMessage = ReactIntl.FormattedMessage;
var FormattedHTMLMessage = ReactIntl.FormattedHTMLMessage;
var Modal = require('../modal/modal.jsx');
var Registration = require('../registration/registration.jsx');
@ -18,7 +15,16 @@ var Intro = React.createClass({
},
getDefaultProps: function () {
return {
projectCount: 10569070
projectCount: 10569070,
messages: {
'intro.aboutScratch': 'ABOUT SCRATCH',
'intro.forEducators': 'FOR EDUCATORS',
'intro.forParents': 'FOR PARENTS',
'intro.joinScratch': 'JOIN SCRATCH',
'intro.seeExamples': 'SEE EXAMPLES',
'intro.tagLine': 'Create stories, games, and animations<br /> Share with others around the world',
'intro.tryItOut': 'TRY IT OUT'
}
};
},
getInitialState: function () {
@ -52,55 +58,49 @@ var Intro = React.createClass({
return (
<div className="intro">
<div className="content">
<h1>
<FormattedHTMLMessage
id='intro.tagLine'
defaultMessage={
'Create stories, games, and animations<br /> ' +
'Share with others around the world'
} />
<h1 dangerouslySetInnerHTML={{__html: this.props.messages['intro.tagLine']}}>
</h1>
<div className="sprites">
<a className="sprite sprite-1" href="/projects/editor/?tip_bar=getStarted">
<img
className="costume costume-1"
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-a.png" />
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-a.png"
alt="Scratch Cat" />
<img
className="costume costume-2"
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-b.png" />
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-b.png"
alt="Scratch Cat" />
<div className="circle"></div>
<div className="text">
<FormattedMessage
id='intro.tryItOut'
defaultMessage='TRY IT OUT' />
{this.props.messages['intro.tryItOut']}
</div>
</a>
<a className="sprite sprite-2" href="/starter_projects/">
<img
className="costume costume-1"
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-a.png" />
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-a.png"
alt="Tera" />
<img
className="costume costume-2"
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-b.png" />
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-b.png"
alt="Tera" />
<div className="circle"></div>
<div className="text">
<FormattedMessage
id='intro.seeExamples'
defaultMessage='SEE EXAMPLES' />
{this.props.messages['intro.seeExamples']}
</div>
</a>
<a className="sprite sprite-3" href="#" onClick={this.handleJoinClick}>
<img
className="costume costume-1"
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-a.png" />
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-a.png"
alt="Gobo" />
<img
className="costume costume-2"
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-b.png" />
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-b.png"
alt="Gobo" />
<div className="circle"></div>
<div className="text">
<FormattedMessage
id='intro.joinScratch'
defaultMessage='JOIN SCRATCH' />
{this.props.messages['intro.joinScratch']}
</div>
<div className="text subtext">( it&rsquo;s free )</div>
</a>
@ -116,25 +116,20 @@ var Intro = React.createClass({
</div>
<div className="links">
<a href="/about/">
<FormattedMessage
id='intro.aboutScratch'
defaultMessage='ABOUT SCRATCH' />
{this.props.messages['intro.aboutScratch']}
</a>
<a href="/educators/">
<FormattedMessage
id='intro.forEducators'
defaultMessage='FOR EDUCATORS' />
{this.props.messages['intro.forEducators']}
</a>
<a className="last" href="/parents/">
<FormattedMessage
id='intro.forParents'
defaultMessage='FOR PARENTS' />
{this.props.messages['intro.forParents']}
</a>
</div>
</div>
<div className="video">
<div className="play-button" onClick={this.showVideo}></div>
<img src="//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png" />
<img src="//cdn.scratch.mit.edu/scratchr2/static/images/hp-video-screenshot.png"
alt="Intro Video" />
</div>
<Modal
className="video-modal"

View file

@ -1,32 +1,31 @@
var classNames = require('classnames');
var React = require('react');
var ReactDOM = require('react-dom');
var Api = require('../../mixins/api.jsx');
var jar = require('../../lib/jar.js');
var languages = require('../../../languages.json');
var Select = require('../forms/select.jsx');
require('./languagechooser.scss');
/**
* Footer dropdown menu that allows one to change their language.
*/
var LanguageChooser = React.createClass({
type: 'LanguageChooser',
mixins: [
Api
],
getInitialState: function () {
return {
choice: window._locale
};
},
getDefaultProps: function () {
return {
languages: languages
languages: languages,
locale: window._locale
};
},
onSetLanguage: function (e) {
e.preventDefault();
this.setState({'choice': e.target.value});
ReactDOM.findDOMNode(this.refs.languageForm).submit();
jar.set('scratchlanguage', e.target.value);
window.location.reload();
},
render: function () {
var classes = classNames(
@ -35,15 +34,15 @@ var LanguageChooser = React.createClass({
);
return (
<form ref="languageForm" className={classes} action="/i18n/setlang/" method="POST">
<Select name="language" defaultValue={this.state.choice} onChange={this.onSetLanguage}>
<div className={classes}>
<Select name="language" defaultValue={this.props.locale} onChange={this.onSetLanguage}>
{Object.keys(this.props.languages).map(function (value) {
return <option value={value} key={value}>
{this.props.languages[value]}
</option>;
}.bind(this))}
</Select>
</form>
</div>
);
}
});

View file

@ -25,6 +25,9 @@ var defaultStyle = {
}
};
/**
* Container for pop up windows (See: registration window)
*/
var Modal = React.createClass({
type: 'Modal',
statics: {

View file

@ -4,7 +4,6 @@ var ReactIntl = require('react-intl');
var defineMessages = ReactIntl.defineMessages;
var FormattedMessage = ReactIntl.FormattedMessage;
var injectIntl = ReactIntl.injectIntl;
var xhr = require('xhr');
var Api = require('../../mixins/api.jsx');
var Avatar = require('../avatar/avatar.jsx');
@ -48,14 +47,14 @@ var Navigation = React.createClass({
loginOpen: false,
loginError: null,
registrationOpen: false,
unreadMessageCount: 0,
messageCountIntervalId: -1
unreadMessageCount: 0, // bubble number to display how many notifications someone has.
messageCountIntervalId: -1 // javascript method interval id for getting messsage count.
};
},
componentDidMount: function () {
if (this.state.session.user) {
this.getMessageCount();
var intervalId = setInterval(this.getMessageCount, 120000);
var intervalId = setInterval(this.getMessageCount, 120000); // check for new messages every 2 mins.
this.setState({'messageCountIntervalId': intervalId});
}
},
@ -96,11 +95,11 @@ var Navigation = React.createClass({
getMessageCount: function () {
this.api({
method: 'get',
uri: '/proxy/users/' + this.state.session.user.username + '/activity/count'
uri: '/users/' + this.state.session.user.username + '/messages/count'
}, function (err, body) {
if (err) return this.setState({'unreadMessageCount': 0});
if (body) {
var count = parseInt(body.msg_count, this.state.unreadMessageCount);
var count = parseInt(body.count, 10);
return this.setState({'unreadMessageCount': count});
}
}.bind(this));
@ -151,16 +150,15 @@ var Navigation = React.createClass({
},
handleLogOut: function (e) {
e.preventDefault();
xhr({
this.api({
host: '',
uri: '/accounts/logout/'
method: 'post',
uri: '/accounts/logout/',
useCsrf: true
}, function (err) {
if (err) {
log.error(err);
} else {
this.closeLogin();
window.refreshSession();
}
if (err) log.error(err);
this.closeLogin();
window.refreshSession();
}.bind(this));
},
handleAccountNavClick: function (e) {
@ -196,8 +194,8 @@ var Navigation = React.createClass({
return (
<div className={classes}>
<ul>
<li className="logo"><a href="/"></a></li>
<li className="logo"><a href="/" aria-label="Scratch"></a></li>
<li className="link create">
<a href="/projects/editor">
<FormattedMessage
@ -237,7 +235,10 @@ var Navigation = React.createClass({
<li className="search">
<form action="/search/google_results" method="get">
<Input type="submit" value="" />
<Input type="text" placeholder={formatMessage(defaultMessages.search)} name="q" />
<Input type="text"
aria-label={formatMessage(defaultMessages.search)}
placeholder={formatMessage(defaultMessages.search)}
name="q" />
<Input type="hidden" name="date" value="anytime" />
<Input type="hidden" name="sort_by" value="datetime_shared" />
</form>
@ -262,7 +263,7 @@ var Navigation = React.createClass({
</li>,
<li className="link right account-nav" key="account-nav">
<a className="userInfo" href="#" onClick={this.handleAccountNavClick}>
<Avatar src={this.state.session.user.thumbnailUrl} />
<Avatar src={this.state.session.user.thumbnailUrl} alt="" />
{this.state.session.user.username}
</a>
<Dropdown

View file

@ -1,23 +1,9 @@
var React = require('react');
var ReactIntl = require('react-intl');
var defineMessages = ReactIntl.defineMessages;
var injectIntl = ReactIntl.injectIntl;
var Box = require('../box/box.jsx');
require('./news.scss');
var defaultMessages = defineMessages({
scratchNews: {
id: 'news.scratchNews',
defaultMessage: 'Scratch News'
},
viewAll: {
id: 'general.viewAll',
defaultMessage: 'View All'
}
});
var News = React.createClass({
type: 'News',
propTypes: {
@ -25,16 +11,19 @@ var News = React.createClass({
},
getDefaultProps: function () {
return {
items: require('./news.json')
items: require('./news.json'),
messages: {
'general.viewAll': 'View All',
'news.scratchNews': 'Scratch News'
}
};
},
render: function () {
var formatMessage = this.props.intl.formatMessage;
return (
<Box
className="news"
title={formatMessage(defaultMessages.scratchNews)}
moreTitle={formatMessage(defaultMessages.viewAll)}
title={this.props.messages['news.scratchNews']}
moreTitle={this.props.messages['general.viewAll']}
moreHref="/discuss/5/">
<ul>
@ -42,7 +31,7 @@ var News = React.createClass({
return (
<li key={item.id}>
<a href={item.url}>
<img src={item.image} width="53" height="53" />
<img src={item.image} width="53" height="53" alt="" />
<h4>{item.headline}</h4>
<p>{item.copy}</p>
</a>
@ -55,4 +44,4 @@ var News = React.createClass({
}
});
module.exports = injectIntl(News);
module.exports = News;

View file

@ -19,7 +19,6 @@
a {
display: block;
text-decoration: none;
white-space: normal;
&:hover {
text-decoration: none;

View file

@ -3,6 +3,10 @@ var React = require('react');
require('./subnavigation.scss');
/**
* Container for a custom, horizontal list of navigation elements
* that can be displayed within a view or component.
*/
var SubNavigation = React.createClass({
type: 'SubNavigation',
render: function () {

View file

@ -15,7 +15,8 @@ var Thumbnail = React.createClass({
src: '',
type: 'project',
showLoves: false,
showRemixes: false
showRemixes: false,
alt: ''
};
},
render: function () {
@ -57,7 +58,7 @@ var Thumbnail = React.createClass({
return (
<div className={classes} >
<a className="thumbnail-image" href={this.props.href}>
<img src={this.props.src} />
<img src={this.props.src} alt={this.props.alt} />
</a>
<div className="thumbnail-title">
<a href={this.props.href}>{this.props.title}</a>

View file

@ -1,8 +1,4 @@
var React = require('react');
var ReactIntl = require('react-intl');
var injectIntl = ReactIntl.injectIntl;
var FormattedMessage = ReactIntl.FormattedMessage;
var Box = require('../box/box.jsx');
@ -13,10 +9,19 @@ var Welcome = React.createClass({
propTypes: {
onDismiss: React.PropTypes.func
},
getDefaultProps: function () {
return {
messages: {
'welcome.welcomeToScratch': 'Welcome to Scratch!',
'welcome.learn': 'Learn how to make a project in Scratch',
'welcome.tryOut': 'Try out starter projects',
'welcome.connect': 'Connect with other Scratchers'
}
};
},
render: function () {
var formatMessage = this.props.intl.formatMessage;
return (
<Box title={formatMessage({id: 'welcome.welcomeToScratch', defaultMessage: 'Welcome to Scratch!'})}
<Box title={this.props.messages['welcome.welcomeToScratch']}
className="welcome"
moreTitle="x"
moreHref="#"
@ -29,37 +34,31 @@ var Welcome = React.createClass({
<div className="welcome-col blue">
<h4>
<a href="/projects/editor/?tip_bar=getStarted">
<FormattedMessage
id="welcome.learn"
defaultMessage="Learn how to make a project in Scratch" />
{this.props.messages['welcome.learn']}
</a>
</h4>
<a href="/projects/editor/?tip_bar=getStarted">
<img src="/images/welcome-learn.png" />
<img src="/images/welcome-learn.png" alt="Get Started" />
</a>
</div>
<div className="welcome-col green">
<h4>
<a href="/starter_projects/">
<FormattedMessage
id="welcome.tryOut"
defaultMessage="Try out starter projects" />
{this.props.messages['welcome.tryOut']}
</a>
</h4>
<a href="/starter_projects/">
<img src="/images/welcome-try.png" />
<img src="/images/welcome-try.png" alt="Starter Projects" />
</a>
</div>
<div className="welcome-col pink">
<h4>
<a href="/studios/146521/">
<FormattedMessage
id="welcome.connect"
defaultMessage="Connect with other Scratchers" />
{this.props.messages['welcome.connect']}
</a>
</h4>
<a href="/studios/146521/">
<img src="/images/welcome-connect.png" />
<img src="/images/welcome-connect.png" alt="Connect" />
</a>
</div>
</Box>
@ -67,4 +66,4 @@ var Welcome = React.createClass({
}
});
module.exports = injectIntl(Welcome);
module.exports = Welcome;

View file

@ -1,8 +1,6 @@
var api = require('./mixins/api.jsx').api;
var jar = require('./lib/jar');
var translations = require('../locales/translations.json');
/**
* -----------------------------------------------------------------------------
* Session
@ -37,10 +35,14 @@ var translations = require('../locales/translations.json');
host: '',
uri: '/session/'
}, function (err, body) {
if (body.banned) {
return window.location = body.redirectUrl;
} else {
window.updateSession(body);
if (err) return;
if (typeof body !== 'undefined') {
if (body.banned) {
return window.location = body.redirectUrl;
} else {
window.updateSession(body);
}
}
});
};
@ -64,18 +66,12 @@ var translations = require('../locales/translations.json');
var obj = jar.get('scratchlanguage');
if (typeof obj === 'undefined') {
obj = window.navigator.userLanguage || window.navigator.language;
}
if (typeof translations[obj] === 'undefined') {
// Fall back on the split
obj = obj.split('-')[0];
}
if (typeof translations[obj] === 'undefined') {
// Language appears to not be supported return `null`
obj = null;
if (['pt','pt-pt','PT','PT-PT'].indexOf(obj) !== -1) {
obj = 'pt-br'; // default Portuguese users to Brazilian Portuguese due to our user base. Added in 2.2.5.
}
}
return obj;
}
window._locale = updateLocale() || 'en';
window._translations = translations;
window._locale = updateLocale();
})();

64
src/l10n.json Normal file
View file

@ -0,0 +1,64 @@
{
"general.accountSettings": "Account settings",
"general.about": "About",
"general.aboutScratch": "About Scratch",
"general.donate": "Donate",
"general.collaborators": "Collaborators",
"general.community": "Community",
"general.contactUs": "Contact Us",
"general.copyright": "Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab",
"general.create": "Create",
"general.credits": "Credits",
"general.discuss": "Discuss",
"general.dmca": "DMCA",
"general.explore": "Explore",
"general.faq": "FAQ",
"general.forParents": "For Parents",
"general.forEducators": "For Educators",
"general.guidelines": "Community Guidelines",
"general.help": "Help",
"general.jobs": "Jobs",
"general.joinScratch": "Join Scratch",
"general.legal": "Legal",
"general.learnMore": "Learn More",
"general.messages": "Messages",
"general.myClasses": "My Classes",
"general.myStuff": "My Stuff",
"general.offlineEditor": "Offline Editor",
"general.profile": "Profile",
"general.scratchConference": "Scratch Conference",
"general.scratchday": "Scratch Day",
"general.scratchEd": "ScratchEd",
"general.scratchFoundation": "Scratch Foundation",
"general.scratchJr": "ScratchJr",
"general.search": "Search",
"general.signIn": "Sign in",
"general.statistics": "Statistics",
"general.support": "Support",
"general.tipsWindow": "Tips Window",
"general.tipsAnimateYourNameTitle": "Animate Your Name",
"general.tipsBearstack": "Bearstack Story",
"general.tipsDanceTitle": "Dance, Dance, Dance",
"general.tipsGetStarted": "Getting Started",
"general.tipsHideAndSeekTitle": "Hide-and-Seek Game",
"general.tipsPongGame": "Create a Pong Game",
"general.termsOfUse": "Terms of Use",
"general.username": "Username",
"general.viewAll": "View All",
"general.whatsHappening": "What's Happening?",
"general.wiki": "Scratch Wiki",
"footer.about": "About Scratch",
"footer.discuss": "Discussion Forums",
"footer.help": "Help Page",
"footer.scratchFamily": "Scratch Family",
"login.forgotPassword": "Forgot Password?",
"navigation.signOut": "Sign out",
"parents.FaqAgeRangeA": "While Scratch is primarily designed for 8 to 16 year olds, it is also used by people of all ages, including younger children with their parents.",
"parents.FaqAgeRangeQ": "What is the age range for Scratch?",
"parents.FaqResourcesQ": "What resources are available for learning Scratch?",
"parents.introDescription": "Scratch is a programming language and an online community where children can program and share interactive media such as stories, games, and animation with people from all over the world. As children create with Scratch, they learn to think creatively, work collaboratively, and reason systematically. Scratch is designed and maintained by the Lifelong Kindergarten group at the MIT Media Lab."
}

View file

@ -2,6 +2,9 @@ var ReactIntl = require('react-intl');
var customLanguages = require('../../custom-locales.json');
/**
* Add custom locales to react-intl if it doesn't have them.
*/
for (var locale in customLanguages) {
ReactIntl.addLocaleData(customLanguages[locale]);
}

View file

@ -1,6 +1,14 @@
var cookie = require('cookie');
var xhr = require('xhr');
/**
* Module that handles coookie interactions.
* (Cookies?!?! Jar?!?! Get it?!?! WE'RE AMAZING!!!!)
*
* get(name, callback) can be sync or async, as callback is optional
* set(name, value) synchronously sets the cookie
* use(name, uri, callback) can by sync or async, gets cookie from the uri if not there.
*/
var Jar = {};
Jar.get = function (name, callback) {

View file

@ -3,12 +3,47 @@ var ReactDOM = require('react-dom');
var ReactIntl = require('./intl.jsx');
var IntlProvider = ReactIntl.IntlProvider;
require('../main.scss');
var Navigation = require('../components/navigation/navigation.jsx');
var Footer = require('../components/footer/footer.jsx');
var render = function (jsx, element) {
// Get locale and messages from global namespace (see "init.js")
var locale = window._locale;
var messages = window._translations[locale];
var locale = window._locale || 'en';
if (typeof window._messages[locale] === 'undefined') {
// Fall back on the split
locale = locale.split('-')[0];
}
if (typeof window._messages[locale] === 'undefined') {
// Language appears to not be supported fall back to 'en'
locale = 'en';
}
var messages = window._messages[locale];
// Render component
// Render nav and footer for page.
var nav = ReactDOM.render(
<IntlProvider locale={locale} messages={messages}>
<Navigation />
</IntlProvider>,
document.getElementById('navigation')
);
var footer = ReactDOM.render(
<IntlProvider locale={locale} messages={messages}>
<Footer />
</IntlProvider>,
document.getElementById('footer')
);
// Provide list of rendered components
window._renderedComponents = window._renderedComponents || [];
window._renderedComponents.push(nav);
window._renderedComponents.push(footer);
// Render view component
var component = ReactDOM.render(
<IntlProvider locale={locale} messages={messages}>
{jsx}
@ -16,8 +51,6 @@ var render = function (jsx, element) {
element
);
// Provide list of rendered components
window._renderedComponents = window._renderedComponents || [];
window._renderedComponents.push(component);
};

View file

@ -1,9 +0,0 @@
var render = require('./lib/render.jsx');
require('./main.scss');
var Navigation = require('./components/navigation/navigation.jsx');
var Footer = require('./components/footer/footer.jsx');
render(<Navigation />, document.getElementById('navigation'));
render(<Footer />, document.getElementById('footer'));

View file

@ -40,8 +40,10 @@ p.legal {
}
/* Links */
a {
white-space: nowrap;
p {
a {
white-space: nowrap;
}
}
a:link,

View file

@ -6,6 +6,14 @@ var log = require('../lib/log.js');
var CookieMixinFactory = require('./cookieMixinFactory.jsx');
/**
* Component mixin that constructs requests to the scratch api.
* Custom arguments:
* - useCsrf [boolean] handles unique csrf token retrieval for POST requests. This prevents
* CSRF forgeries (see: https://www.squarefree.com/securitytips/web-developers.html#CSRF)
*
* It also takes in other arguments specified in the xhr library spec.
*/
var Api = {
mixins: [
// Provides useScratchcsrftoken
@ -36,7 +44,11 @@ var Api = {
if (err) log.error(err);
// Legacy API responses come as lists, and indicate to redirect the client like
// [{success: true, redirect: "/location/to/redirect"}]
if (body && body[0] && 'redirect' in body[0]) window.location = body[0].redirect;
try {
if ('redirect' in body[0]) window.location = body[0].redirect;
} catch (err) {
// do nothing
}
callback(err, body);
});
}.bind(this);

View file

@ -26,6 +26,7 @@ var About = React.createClass({
<div>
<iframe
title="Scratch Overview Video"
src="https://player.vimeo.com/video/65583694?title=0&byline=0&portrait=0"
frameBorder="0"
webkitAllowFullScreen
@ -38,7 +39,7 @@ var About = React.createClass({
<ul>
<li>
<h2><FormattedMessage id='about.whoUsesScratch' /></h2>
<img src="/images/about/who-uses-scratch.jpg" />
<img src="/images/about/who-uses-scratch.jpg" alt="" />
<p><FormattedHTMLMessage id='about.whoUsesScratchDescription' /></p>
</li>
@ -55,25 +56,25 @@ var About = React.createClass({
<li>
<h2><FormattedMessage id='about.aroundTheWorld' /></h2>
<img src="/images/about/around-the-world.png" />
<img src="/images/about/around-the-world.png" alt="" />
<p><FormattedHTMLMessage id='about.aroundTheWorldDescription' /></p>
</li>
<li>
<h2><FormattedMessage id='about.schools' /></h2>
<img src="/images/about/scratch-in-schools.jpg" />
<img src="/images/about/scratch-in-schools.jpg" alt="" />
<p><FormattedHTMLMessage id='about.schoolsDescription' /></p>
</li>
<li>
<h2><FormattedMessage id='about.quotes' /></h2>
<img src="/images/about/quotes.gif" />
<img src="/images/about/quotes.gif" alt="Quotes about Scratch" />
<p><FormattedHTMLMessage id='about.quotesDescription' /></p>
</li>
<li>
<h2><FormattedMessage id='about.research' /></h2>
<img src="/images/about/research-remix.png" />
<img src="/images/about/research-remix.png" alt="" />
<p><FormattedHTMLMessage id='about.researchDescription' /></p>
</li>
@ -81,10 +82,18 @@ var About = React.createClass({
<h2><FormattedMessage id='about.learnMore' /></h2>
<p>
<ul className="list">
<li><a href="/help"><FormattedMessage id='about.learnMoreHelp' /></a></li>
<li><a href="/info/faq"><FormattedMessage id='about.learnMoreFaq' /></a></li>
<li><a href="/parents"><FormattedMessage id='about.learnMoreParents' /></a></li>
<li><a href="/credits"><FormattedMessage id='about.learnMoreCredits' /></a></li>
<li>
<a href="/help"><FormattedMessage id='about.learnMoreHelp' /></a>
</li>
<li>
<a href="/info/faq"><FormattedMessage id='about.learnMoreFaq' /></a>
</li>
<li>
<a href="/parents"><FormattedMessage id='about.learnMoreParents' /></a>
</li>
<li>
<a href="/info/credits"><FormattedMessage id='about.learnMoreCredits' /></a>
</li>
</ul>
</p>
</li>

26
src/views/about/l10n.json Normal file
View file

@ -0,0 +1,26 @@
{
"about.introOne": "With Scratch, you can program your own interactive stories, games, and animations — and share your creations with others in the online community.",
"about.introTwo": "Scratch helps young people learn to think creatively, reason systematically, and work collaboratively — essential skills for life in the 21st century.",
"about.introThree": "Scratch is a project of the Lifelong Kindergarten Group at the MIT Media Lab. It is provided free of charge.",
"about.introParents": "Info for parents",
"about.introEducators": "Info for educators",
"about.whoUsesScratch": "Who Uses Scratch?",
"about.whoUsesScratchDescription": "Scratch is designed especially for ages 8 to 16, but is used by people of all ages. Millions of people are creating Scratch projects in a wide variety of settings, including homes, schools, museums, libraries, and community centers.",
"about.aroundTheWorld": "Around the World",
"about.aroundTheWorldDescription": "Scratch is used in more than 150 different countries and available in more than 40 languages. To change languages, click the menu at the bottom of the page. Or, in the Project Editor, click the globe at the top of the page. To add or improve a translation, see the <a href=\"http://wiki.scratch.mit.edu/wiki/How_to_Translate_Scratch\">translation</a> page.",
"about.quotes": "Quotes",
"about.quotesDescription": "The Scratch Team has received many emails from youth, parents, and educators expressing thanks for Scratch. Want to see what people are saying? You can read a collection of the <a href=\"/info/quotes\">quotes</a> we&#39;ve received.",
"about.learnMore": "Learn More About Scratch",
"about.learnMoreHelp": "Scratch Help Page",
"about.learnMoreFaq": "Frequently Asked Questions",
"about.learnMoreParents": "Information for Parents",
"about.learnMoreCredits": "Scratch Credits",
"about.literacy": "Learn to Code, Code to Learn",
"about.literacyDescription": "The ability to code computer programs is an important part of literacy in todays society. When people learn to code in Scratch, they learn important strategies for solving problems, designing projects, and communicating ideas.",
"about.schools": "Scratch in Schools",
"about.schoolsDescription": "Students are learning with Scratch at all levels (from elementary school to college) and across disciplines (such as math, computer science, language arts, social studies). Educators share stories, exchange resources, ask questions, and find people on the <a href=\"http://scratched.gse.harvard.edu/\">ScratchEd website</a>.",
"about.research": "Research",
"about.researchDescription": "The MIT Scratch Team and collaborators are researching how people use and learn with Scratch (for an introduction, see <a href=\"http://web.media.mit.edu/~mres/papers/Scratch-CACM-final.pdf\">Scratch: Programming for All</a>). Find out more about Scratch <a href=\"/info/research\">research</a> and <a href=\"/statistics\">statistics</a> about Scratch.",
"about.support": "Support and Funding",
"about.supportDescription": "The Scratch project, initiated in 2003, has received generous support from the National Science Foundation (grants 0325828, 1002713, 1027848, 1019396), Intel Foundation, Microsoft, MacArthur Foundation, LEGO Foundation, Code-to-Learn Foundation, Google, Dell, Fastly, Inversoft, and MIT Media Lab research consortia. If you&#39;d like to support Scratch, please see our <a href=\"https://secure.donationpay.org/scratchfoundation/\">donate page</a>, or contact us at donate@scratch.mit.edu."
}

View file

@ -8,7 +8,6 @@ var Carousel = require('../../components/carousel/carousel.jsx');
var Input = require('../../components/forms/input.jsx');
var Spinner = require('../../components/spinner/spinner.jsx');
require('./components.scss');
var Components = React.createClass({

View file

@ -14,112 +14,112 @@ var Credits = React.createClass({
<ul>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/167_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/167_170x170.png" alt="Mitchel Avatar" />
<span className="name">Mitchel Resnick</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/169_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/169_170x170.png" alt="Natalie Avatar" />
<span className="name">Natalie Rusk</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/573207_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/573207_170x170.png" alt="Sayamindu Avatar" />
<span className="name">Sayamindu Dasgupta</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/13682_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/13682_170x170.png" alt="Ricarose Avatar" />
<span className="name">Ricarose Roque</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/2584924_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/2584924_170x170.png" alt="Ray Avatar" />
<span className="name">Ray Schamp</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/3484484_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/3484484_170x170.png" alt="Eric Avatar" />
<span className="name">Eric Schilling</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/3532363_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/3532363_170x170.png" alt="Chris Avatar" />
<span className="name">Chris Willis-Ford</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/3581881_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/3581881_170x170.png" alt="Carl Avatar" />
<span className="name">Carl Bowman</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/4373707_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/4373707_170x170.png" alt="Matthew Avatar" />
<span className="name">Matthew Taylor</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/4598206_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/4598206_170x170.png" alt="Kasia Avatar" />
<span className="name">Kasia Chmielinski</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/703844_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/703844_170x170.png" alt="Tim Avatar" />
<span className="name">Tim Mickel</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/2752403_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/2752403_170x170.png" alt="Saskia Avatar" />
<span className="name">Saskia Leggett</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/2755634_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/2755634_170x170.png" alt="Christan Avatar" />
<span className="name">Christan Balch</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/5721684_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/5721684_170x170.png" alt="Randy Avatar" />
<span className="name">Randy Jou</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/10866958_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/10866958_170x170.png" alt="Colby Avatar" />
<span className="name">Colby Gutierrez-Kraybill</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/1709047_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/1709047_170x170.png" alt="Andrew Avatar" />
<span className="name">Andrew Sliwinski</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/default_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/default_170x170.png" alt="Ben Avatar" />
<span className="name">Ben Berg</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/2286560_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/2286560_170x170.png" alt="Carmelo Avatar" />
<span className="name">Carmelo Presicce</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/default_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/default_170x170.png" alt="Moran Avatar" />
<span className="name">Moran Tsur</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/3661900_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/3661900_170x170.png" alt="Juanita Avatar" />
<span className="name">Juanita Buitrago</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/default_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/default_170x170.png" alt="Shruti Avatar" />
<span className="name">Shruti Mohnot</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/1915915_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/1915915_170x170.png" alt="Hannah Avatar" />
<span className="name">Hannah Cole</span>
</li>
</ul>
@ -128,47 +128,47 @@ var Credits = React.createClass({
<ul>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/49156_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/49156_170x170.png" alt="Mark Avatar" />
<span className="name">Mark Goff</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/159139_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/159139_170x170.png" alt="Franchette Avatar" />
<span className="name">Franchette Viloria</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/246290_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/246290_170x170.png" alt="Sarah Avatar" />
<span className="name">Sarah Otts</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/2496866_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/2496866_170x170.png" alt="Jolie Avatar" />
<span className="name">Jolie Castellucci</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/3087865_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/3087865_170x170.png" alt="Andrea Avatar" />
<span className="name">Andrea Saxman</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/373646_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/373646_170x170.png" alt="Dalton Avatar" />
<span className="name">Dalton Miner</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/586054_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/586054_170x170.png" alt="Megan Avatar" />
<span className="name">Megan Haddadi</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/4836354_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/4836354_170x170.png" alt="Christina Avatar" />
<span className="name">Christina Huang</span>
</li>
<li>
<img src="//cdn.scratch.mit.edu/get_image/user/4747093_170x170.png" />
<img src="//cdn.scratch.mit.edu/get_image/user/4747093_170x170.png" alt="Annie Avatar" />
<span className="name">Annie Whitehouse</span>
</li>
</ul>

View file

@ -50,7 +50,7 @@ var Hoc = React.createClass({
<div className="card">
<a href="/projects/editor/?tip_bar=name">
<div className="card-info" onMouseEnter={this.onCardEnter.bind(this, 'name-bg')}>
<img src="/images/hoc2015/name-tutorial.jpg" />
<img src="/images/hoc2015/name-tutorial.jpg" alt="" />
<Button>
<FormattedMessage
id='general.tipsAnimateYourNameTitle'
@ -63,7 +63,7 @@ var Hoc = React.createClass({
<div className="card" onMouseEnter={this.onCardEnter.bind(this, 'wbb-bg')}>
<a href="/hide/">
<div className="card-info">
<img src="/images/hoc2015/hide-seek-tutorial.jpg" />
<img src="/images/hoc2015/hide-seek-tutorial.jpg" alt="" />
<Button>
<FormattedMessage
id='general.tipsHideAndSeekTitle'
@ -76,7 +76,7 @@ var Hoc = React.createClass({
<div className="card" onMouseEnter={this.onCardEnter.bind(this, 'dance-bg')}>
<a href="/projects/editor/?tip_bar=dance">
<div className="card-info">
<img src="/images/hoc2015/dance-tutorial.jpg" />
<img src="/images/hoc2015/dance-tutorial.jpg" alt="" />
<Button>
<FormattedMessage
id='general.tipsDanceTitle'
@ -142,7 +142,7 @@ var Hoc = React.createClass({
</div>
<div className="resource">
<img src="/svgs/tips-card.svg" />
<img src="/svgs/tips-card.svg" alt="" />
<div className="resource-info">
<h5>
<FormattedMessage
@ -163,7 +163,7 @@ var Hoc = React.createClass({
</div>
<div className="resource">
<img src="/svgs/tips-card.svg" />
<img src="/svgs/tips-card.svg" alt="" />
<div className="resource-info">
<h5>
<FormattedMessage
@ -184,7 +184,7 @@ var Hoc = React.createClass({
</div>
<div className="resource">
<img src="/svgs/tips-card.svg" />
<img src="/svgs/tips-card.svg" alt="" />
<div className="resource-info">
<h5>
<FormattedMessage
@ -223,7 +223,7 @@ var Hoc = React.createClass({
</p>
</div>
<div className="column">
<img src="/images/hoc2015/tips-test-animation.gif" />
<img src="/images/hoc2015/tips-test-animation.gif" alt="Tips Window Animation" />
</div>
</section>
@ -248,7 +248,7 @@ var Hoc = React.createClass({
<div className="card">
<a href="/projects/editor/?tip_bar=getStarted">
<div className="card-info">
<img src="/images/hoc2015/getting-started-tutorial.jpg" />
<img src="/images/hoc2015/getting-started-tutorial.jpg" alt="" />
<Button>
<FormattedMessage
id='general.tipsGetStarted'
@ -261,7 +261,7 @@ var Hoc = React.createClass({
<div className="card">
<a href="/bearstack/">
<div className="card-info">
<img src="/images/hoc2015/bearstack-tutorial.jpg" />
<img src="/images/hoc2015/bearstack-tutorial.jpg" alt="" />
<Button>
<FormattedMessage
id='general.tipsBearstack'
@ -274,7 +274,7 @@ var Hoc = React.createClass({
<div className="card">
<a href="/hoops">
<div className="card-info">
<img src="/images/hoc2015/bball-tutorial.jpg" />
<img src="/images/hoc2015/bball-tutorial.jpg" alt="" />
<Button>
<FormattedMessage
id='general.tipsBBallHoops'
@ -305,7 +305,7 @@ var Hoc = React.createClass({
</div>
<div className="studio">
<img src="/svgs/studio.svg" />
<img src="/svgs/studio.svg" alt="" />
<div className="studio-info">
<a href="/studios/432299/">
<h5>
@ -318,7 +318,7 @@ var Hoc = React.createClass({
</div>
<div className="studio">
<img src="/svgs/studio.svg" />
<img src="/svgs/studio.svg" alt="" />
<div className="studio-info">
<a href="/studios/1672166/">
<h5>
@ -331,7 +331,7 @@ var Hoc = React.createClass({
</div>
<div className="studio">
<img src="/svgs/studio.svg" />
<img src="/svgs/studio.svg" alt="" />
<div className="studio-info">
<a href="/studios/1065372/">
<h5>
@ -344,7 +344,7 @@ var Hoc = React.createClass({
</div>
<div className="studio">
<img src="/svgs/studio.svg" />
<img src="/svgs/studio.svg" alt="" />
<div className="studio-info">
<a href="/studios/1672164/">
<h5>
@ -366,22 +366,22 @@ var Hoc = React.createClass({
</h3>
<div className="logos">
<a href="http://scratched.gse.harvard.edu/">
<img src="/images/hoc2015/scratchEd-logo.png" />
<img src="/images/hoc2015/scratchEd-logo.png" alt="ScratchEd" />
</a>
<a href="https://code.org/">
<img src="/images/hoc2015/code-org-logo.png" />
<img src="/images/hoc2015/code-org-logo.png" alt="code.org" />
</a>
<a href="http://www.cartoonnetwork.com/">
<img src="/images/hoc2015/cn-logo.png" />
<img src="/images/hoc2015/cn-logo.png" alt="Cartoon Network" />
</a>
<a href="http://www.madewithcode.com/">
<img src="/images/hoc2015/made-with-code-logo.png" />
<img src="/images/hoc2015/made-with-code-logo.png" alt="Made with Code" />
</a>
<a href="http://www.paalive.org/">
<img src="/images/hoc2015/paa-logo.png" />
<img src="/images/hoc2015/paa-logo.png" alt="Progressive Arts Alliance" />
</a>
<a href="http://www.catrobat.org/">
<img src="/images/hoc2015/pocketcode-logo.png" />
<img src="/images/hoc2015/pocketcode-logo.png" alt="Pocket Code" />
</a>
</div>

View file

@ -77,10 +77,6 @@ $base-bg: $ui-white;
min-width: 200px;
max-width: 230px;
a {
white-space: normal;
}
.card-info {
border-radius: 5px;
background-color: $base-bg;
@ -203,10 +199,6 @@ $base-bg: $ui-white;
.studio {
width: 50%;
a {
white-space: normal;
}
h5 {
width: 200px;
}
@ -239,10 +231,6 @@ $base-bg: $ui-white;
margin: 20px 0;
width: 100%;
a {
white-space: normal;
}
img {
margin: 20px;
max-width: 150px;

19
src/views/hoc/l10n.json Normal file
View file

@ -0,0 +1,19 @@
{
"hoc.activityCards": "Activity Cards",
"hoc.activityCardsHeader": "Activity Cards and Guides",
"hoc.activityCardsInfo1": "Want tips and ideas for these Hour of Code&trade; activities? Use the activity cards to get ideas for creating with Scratch. Facilitator guides can help you plan a group activity.",
"hoc.addToStudios": "Add Your Projects to Studios",
"hoc.addToStudiosDescription": "These studios include projects created by young people around the world. Take a look at the studios to get inspired - or submit your own projects to the studios!",
"hoc.facilitatorGuide": "Facilitator Guide",
"hoc.findOutMore": "Find out more",
"hoc.helpScratch": "Help with Scratch",
"hoc.helpScratchDescription": "You can find tutorials and helpful hints in the <a href=\"/projects/editor/?tip_bar=home\">Tips Window</a>. For more resources, see <a href=\"/help\">Scratch Help</a>",
"hoc.moreActivities": "Want More Activities?",
"hoc.moreDescription": "Check out these other tutorials. Or remix one of our <a href=\"/starter_projects\">Starter Projects</a>",
"hoc.officialNotice": "The \"Hour of Code&trade;\" is a nationwide initiative by <a href=\"http://csedweek.org\">Computer Science Education Week</a> and <a href=\"http://code.org\">Code.org</a> to introduce millions of students to one hour of computer science and computer programming.",
"hoc.studioAlice": "Alice in Wonderland Studio",
"hoc.studioWeBareBears": "We Bare Bears Studio",
"hoc.subTitle": "With Scratch, you can program your own stories, games, and animations — and share them online.",
"hoc.tipsDescription": "Need help getting started? Looking for ideas?&nbsp; You can find tutorials and helpful hints in the <a href=\"/projects/editor/?tip_bar=home\">Tips Window</a>",
"hoc.title": "Get Creative with Coding"
}

View file

@ -0,0 +1,28 @@
{
"splash.featuredProjects": "Featured Projects",
"splash.featuredStudios": "Featured Studios",
"splash.projectsCuratedBy": "Projects Curated by",
"splash.scratchDesignStudioTitle": "Scratch Design Studio",
"splash.visitTheStudio": "Visit the studio",
"splash.recentlySharedProjects": "Recently Shared Projects",
"splash.projectsByScratchersFollowing": "Projects by Scratchers I'm Following",
"splash.projectsLovedByScratchersFollowing": "Projects Loved by Scratchers I'm Following",
"splash.projectsInStudiosFollowing": "Projects in Studios I'm Following",
"splash.communityRemixing": "What the Community is Remixing",
"splash.communityLoving": "What the Community is Loving",
"intro.aboutScratch": "ABOUT SCRATCH",
"intro.forEducators": "FOR EDUCATORS",
"intro.forParents": "FOR PARENTS",
"intro.joinScratch": "JOIN SCRATCH",
"intro.seeExamples": "SEE EXAMPLES",
"intro.tagLine": "Create stories, games, and animations<br /> Share with others around the world",
"intro.tryItOut": "TRY IT OUT",
"news.scratchNews": "Scratch News",
"welcome.welcomeToScratch": "Welcome to Scratch!",
"welcome.learn": "Learn how to make a project in Scratch",
"welcome.tryOut": "Try out starter projects",
"welcome.connect": "Connect with other Scratchers"
}

View file

@ -28,11 +28,11 @@ var Splash = injectIntl(React.createClass({
getInitialState: function () {
return {
projectCount: 10569070,
activity: [],
news: [],
featuredCustom: {},
featuredGlobal: {},
showEmailConfirmationModal: false,
activity: [], // recent social actions taken by users someone is following
news: [], // gets news posts from the scratch Tumblr
featuredCustom: {}, // custom homepage rows, such as "Projects by Scratchers I'm Following"
featuredGlobal: {}, // global homepage rows, such as "Featured Projects"
showEmailConfirmationModal: false, // flag that determines whether to show banner to request email conf.
refreshCacheStatus: 'notrequested'
};
},
@ -318,6 +318,25 @@ var Splash = injectIntl(React.createClass({
var featured = this.renderHomepageRows();
var emailConfirmationStyle = {width: 500, height: 330, padding: 1};
var homepageCacheState = this.getHomepageRefreshStatus();
var formatMessage = this.props.intl.formatMessage;
var formatHTMLMessage = this.props.intl.formatHTMLMessage;
var messages = {
'general.viewAll': formatMessage({id: 'general.viewAll'}),
'news.scratchNews': formatMessage({id: 'news.scratchNews'}),
'welcome.welcomeToScratch': formatMessage({id: 'welcome.welcomeToScratch'}),
'welcome.learn': formatMessage({id: 'welcome.learn'}),
'welcome.tryOut': formatMessage({id: 'welcome.tryOut'}),
'welcome.connect': formatMessage({id: 'welcome.connect'}),
'intro.aboutScratch': formatMessage({id: 'intro.aboutScratch'}),
'intro.forEducators': formatMessage({id: 'intro.forEducators'}),
'intro.forParents': formatMessage({id: 'intro.forParents'}),
'intro.joinScratch': formatMessage({id: 'intro.joinScratch'}),
'intro.seeExamples': formatMessage({id: 'intro.seeExamples'}),
'intro.tagLine': formatHTMLMessage({id: 'intro.tagLine'}),
'intro.tryItOut': formatMessage({id: 'intro.tryItOut'})
};
return (
<div className="splash">
{this.shouldShowEmailConfirmation() ? [
@ -341,14 +360,16 @@ var Splash = injectIntl(React.createClass({
{this.state.session.user ? [
<div key="header" className="splash-header">
{this.shouldShowWelcome() ? [
<Welcome key="welcome" onDismiss={this.handleDismiss.bind(this, 'welcome')}/>
<Welcome key="welcome"
onDismiss={this.handleDismiss.bind(this, 'welcome')}
messages={messages} />
] : [
<Activity key="activity" items={this.state.activity} />
]}
<News items={this.state.news} />
<News items={this.state.news} messages={messages} />
</div>
] : [
<Intro projectCount={this.state.projectCount} key="intro"/>
<Intro projectCount={this.state.projectCount} messages={messages} key="intro"/>
]}
{featured}

View file

@ -3,7 +3,7 @@ var path = require('path');
var po2icu = require('po2icu');
var tap = require('tap');
var buildLocales = require('../../lib/locale-compare');
var buildLocales = require('../../bin/lib/locale-compare');
tap.test('buildLocalesFile', function (t) {
var md5map = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../fixtures/test_es_md5map.json'), 'utf8'));

View file

@ -2,7 +2,7 @@ var fs = require('fs');
var path = require('path');
var tap = require('tap');
var buildLocales = require('../../lib/locale-compare');
var buildLocales = require('../../bin/lib/locale-compare');
tap.test('buildLocalesFile', function (t) {
var actualMd5map = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../fixtures/test_es_md5map.json'), 'utf8'));

View file

@ -1,6 +1,6 @@
var tap = require('tap');
var buildLocales = require('../../lib/locale-compare');
var buildLocales = require('../../bin/lib/locale-compare');
tap.test('buildLocalesMergeTranslations', function (t) {
var existingTranslations = {

View file

@ -1,6 +1,6 @@
var tap = require('tap');
var buildLocales = require('../../lib/locale-compare');
var buildLocales = require('../../bin/lib/locale-compare');
tap.test('buildLocalesGetMD5', function (t) {
var testString1 = 'are there bears here?';

View file

@ -6,8 +6,7 @@ var routes = require('./server/routes.json');
// Prepare all entry points
var entry = {
init: './src/init.js',
main: './src/main.jsx'
init: './src/init.js'
};
routes.forEach(function (route) {
entry[route.view] = './src/views/' + route.view + '/' + route.view + '.jsx';
@ -53,7 +52,8 @@ module.exports = {
},
plugins: [
new CopyWebpackPlugin([
{from: 'static'}
{from: 'static'},
{from: 'intl', to: 'js'}
]),
new webpack.optimize.UglifyJsPlugin({
compress: {