mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 07:38:07 -05:00
Merge pull request #305 from mewtaylor/feature/266-componentize-locale-strings
GH-266: view-ify locale strings
This commit is contained in:
commit
9e58efc7e2
30 changed files with 426 additions and 329 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -10,6 +10,7 @@ npm-*
|
||||||
|
|
||||||
# Locales
|
# Locales
|
||||||
/locales
|
/locales
|
||||||
|
/intl
|
||||||
|
|
||||||
# Elastic Beanstalk Files
|
# Elastic Beanstalk Files
|
||||||
.elasticbeanstalk/*
|
.elasticbeanstalk/*
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -18,8 +18,9 @@ build:
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf ./build
|
rm -rf ./build
|
||||||
|
rm -rf ./intl
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
mkdir -p locales
|
mkdir -p intl
|
||||||
|
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
|
@ -38,7 +39,7 @@ tag:
|
||||||
echo $(GIT_VERSION) > ./build/version.txt
|
echo $(GIT_VERSION) > ./build/version.txt
|
||||||
|
|
||||||
translations:
|
translations:
|
||||||
./lib/bin/build-locales locales/translations.json
|
./bin/build-locales intl
|
||||||
|
|
||||||
webpack:
|
webpack:
|
||||||
$(WEBPACK) --bail
|
$(WEBPACK) --bail
|
||||||
|
@ -63,7 +64,6 @@ lint:
|
||||||
$(ESLINT) ./*.js
|
$(ESLINT) ./*.js
|
||||||
$(ESLINT) ./server/*.js
|
$(ESLINT) ./server/*.js
|
||||||
$(ESLINT) ./src/*.js
|
$(ESLINT) ./src/*.js
|
||||||
$(ESLINT) ./src/*.jsx
|
|
||||||
$(ESLINT) ./src/mixins/*.jsx
|
$(ESLINT) ./src/mixins/*.jsx
|
||||||
$(ESLINT) ./src/views/**/*.jsx
|
$(ESLINT) ./src/views/**/*.jsx
|
||||||
$(ESLINT) ./src/components/**/*.jsx
|
$(ESLINT) ./src/components/**/*.jsx
|
||||||
|
|
161
bin/build-locales
Executable file
161
bin/build-locales
Executable file
|
@ -0,0 +1,161 @@
|
||||||
|
#!/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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 view in viewLocales) {
|
||||||
|
var viewTranslations = merge(viewLocales[view], generalLocales);
|
||||||
|
var objectString = JSON.stringify(viewTranslations);
|
||||||
|
var fileString = 'window._messages = ' + objectString + ';';
|
||||||
|
fs.writeFileSync(outputDir + '/' + view + '.intl.js', fileString);
|
||||||
|
}
|
||||||
|
});
|
133
en.json
133
en.json
|
@ -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'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 today’s 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'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™ 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™\" 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? 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"
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
});
|
|
|
@ -46,6 +46,7 @@
|
||||||
"jsx-loader": "0.13.2",
|
"jsx-loader": "0.13.2",
|
||||||
"lodash.clone": "3.0.3",
|
"lodash.clone": "3.0.3",
|
||||||
"lodash.defaultsdeep": "3.10.0",
|
"lodash.defaultsdeep": "3.10.0",
|
||||||
|
"lodash.merge": "3.3.2",
|
||||||
"lodash.omit": "3.1.0",
|
"lodash.omit": "3.1.0",
|
||||||
"lodash.range": "3.0.1",
|
"lodash.range": "3.0.1",
|
||||||
"minilog": "2.0.8",
|
"minilog": "2.0.8",
|
||||||
|
|
|
@ -37,7 +37,7 @@ app.use(log());
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
if (isProduction) {
|
if (isProduction) {
|
||||||
app.use(express.static(path.resolve(__dirname, '../build'), {
|
app.use(express.static(path.resolve(__dirname, '../build'), {
|
||||||
lastModified: true,
|
etag: 'strong',
|
||||||
maxAge: '1y'
|
maxAge: '1y'
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
<script src="/js/lib/react-intl-with-locales{{min}}.js"></script>
|
<script src="/js/lib/react-intl-with-locales{{min}}.js"></script>
|
||||||
<script src="/js/lib/raven.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>
|
<script src="/js/{{view}}.bundle.js"></script>
|
||||||
|
|
||||||
<!-- Error logging (Sentry) -->
|
<!-- Error logging (Sentry) -->
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
var omit = require('lodash.omit');
|
var omit = require('lodash.omit');
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var ReactIntl = require('react-intl');
|
|
||||||
var FormattedMessage = ReactIntl.FormattedMessage;
|
|
||||||
var FormattedHTMLMessage = ReactIntl.FormattedHTMLMessage;
|
|
||||||
|
|
||||||
var Modal = require('../modal/modal.jsx');
|
var Modal = require('../modal/modal.jsx');
|
||||||
var Registration = require('../registration/registration.jsx');
|
var Registration = require('../registration/registration.jsx');
|
||||||
|
@ -18,7 +15,16 @@ var Intro = React.createClass({
|
||||||
},
|
},
|
||||||
getDefaultProps: function () {
|
getDefaultProps: function () {
|
||||||
return {
|
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 () {
|
getInitialState: function () {
|
||||||
|
@ -52,13 +58,7 @@ var Intro = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className="intro">
|
<div className="intro">
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<h1>
|
<h1 dangerouslySetInnerHTML={{__html: this.props.messages['intro.tagLine']}}>
|
||||||
<FormattedHTMLMessage
|
|
||||||
id='intro.tagLine'
|
|
||||||
defaultMessage={
|
|
||||||
'Create stories, games, and animations<br /> ' +
|
|
||||||
'Share with others around the world'
|
|
||||||
} />
|
|
||||||
</h1>
|
</h1>
|
||||||
<div className="sprites">
|
<div className="sprites">
|
||||||
<a className="sprite sprite-1" href="/projects/editor/?tip_bar=getStarted">
|
<a className="sprite sprite-1" href="/projects/editor/?tip_bar=getStarted">
|
||||||
|
@ -70,9 +70,7 @@ var Intro = React.createClass({
|
||||||
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-b.png" />
|
src="//cdn.scratch.mit.edu/scratchr2/static/images/cat-b.png" />
|
||||||
<div className="circle"></div>
|
<div className="circle"></div>
|
||||||
<div className="text">
|
<div className="text">
|
||||||
<FormattedMessage
|
{this.props.messages['intro.tryItOut']}
|
||||||
id='intro.tryItOut'
|
|
||||||
defaultMessage='TRY IT OUT' />
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a className="sprite sprite-2" href="/starter_projects/">
|
<a className="sprite sprite-2" href="/starter_projects/">
|
||||||
|
@ -84,9 +82,7 @@ var Intro = React.createClass({
|
||||||
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-b.png" />
|
src="//cdn.scratch.mit.edu/scratchr2/static/images/tera-b.png" />
|
||||||
<div className="circle"></div>
|
<div className="circle"></div>
|
||||||
<div className="text">
|
<div className="text">
|
||||||
<FormattedMessage
|
{this.props.messages['intro.seeExamples']}
|
||||||
id='intro.seeExamples'
|
|
||||||
defaultMessage='SEE EXAMPLES' />
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a className="sprite sprite-3" href="#" onClick={this.handleJoinClick}>
|
<a className="sprite sprite-3" href="#" onClick={this.handleJoinClick}>
|
||||||
|
@ -98,9 +94,7 @@ var Intro = React.createClass({
|
||||||
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-b.png" />
|
src="//cdn.scratch.mit.edu/scratchr2/static/images/gobo-b.png" />
|
||||||
<div className="circle"></div>
|
<div className="circle"></div>
|
||||||
<div className="text">
|
<div className="text">
|
||||||
<FormattedMessage
|
{this.props.messages['intro.joinScratch']}
|
||||||
id='intro.joinScratch'
|
|
||||||
defaultMessage='JOIN SCRATCH' />
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text subtext">( it’s free )</div>
|
<div className="text subtext">( it’s free )</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -116,19 +110,13 @@ var Intro = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
<div className="links">
|
<div className="links">
|
||||||
<a href="/about/">
|
<a href="/about/">
|
||||||
<FormattedMessage
|
{this.props.messages['intro.aboutScratch']}
|
||||||
id='intro.aboutScratch'
|
|
||||||
defaultMessage='ABOUT SCRATCH' />
|
|
||||||
</a>
|
</a>
|
||||||
<a href="/educators/">
|
<a href="/educators/">
|
||||||
<FormattedMessage
|
{this.props.messages['intro.forEducators']}
|
||||||
id='intro.forEducators'
|
|
||||||
defaultMessage='FOR EDUCATORS' />
|
|
||||||
</a>
|
</a>
|
||||||
<a className="last" href="/parents/">
|
<a className="last" href="/parents/">
|
||||||
<FormattedMessage
|
{this.props.messages['intro.forParents']}
|
||||||
id='intro.forParents'
|
|
||||||
defaultMessage='FOR PARENTS' />
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
var classNames = require('classnames');
|
var classNames = require('classnames');
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var ReactDOM = require('react-dom');
|
|
||||||
|
|
||||||
var Api = require('../../mixins/api.jsx');
|
var Api = require('../../mixins/api.jsx');
|
||||||
|
var jar = require('../../lib/jar.js');
|
||||||
var languages = require('../../../languages.json');
|
var languages = require('../../../languages.json');
|
||||||
var Select = require('../forms/select.jsx');
|
var Select = require('../forms/select.jsx');
|
||||||
|
|
||||||
|
@ -13,20 +13,16 @@ var LanguageChooser = React.createClass({
|
||||||
mixins: [
|
mixins: [
|
||||||
Api
|
Api
|
||||||
],
|
],
|
||||||
getInitialState: function () {
|
|
||||||
return {
|
|
||||||
choice: window._locale
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getDefaultProps: function () {
|
getDefaultProps: function () {
|
||||||
return {
|
return {
|
||||||
languages: languages
|
languages: languages,
|
||||||
|
locale: window._locale
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
onSetLanguage: function (e) {
|
onSetLanguage: function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.setState({'choice': e.target.value});
|
jar.set('scratchlanguage', e.target.value);
|
||||||
ReactDOM.findDOMNode(this.refs.languageForm).submit();
|
window.location.reload();
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var classes = classNames(
|
var classes = classNames(
|
||||||
|
@ -35,15 +31,15 @@ var LanguageChooser = React.createClass({
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form ref="languageForm" className={classes} action="/i18n/setlang/" method="POST">
|
<div className={classes}>
|
||||||
<Select name="language" defaultValue={this.state.choice} onChange={this.onSetLanguage}>
|
<Select name="language" defaultValue={this.props.locale} onChange={this.onSetLanguage}>
|
||||||
{Object.keys(this.props.languages).map(function (value) {
|
{Object.keys(this.props.languages).map(function (value) {
|
||||||
return <option value={value} key={value}>
|
return <option value={value} key={value}>
|
||||||
{this.props.languages[value]}
|
{this.props.languages[value]}
|
||||||
</option>;
|
</option>;
|
||||||
}.bind(this))}
|
}.bind(this))}
|
||||||
</Select>
|
</Select>
|
||||||
</form>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,23 +1,9 @@
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var ReactIntl = require('react-intl');
|
|
||||||
var defineMessages = ReactIntl.defineMessages;
|
|
||||||
var injectIntl = ReactIntl.injectIntl;
|
|
||||||
|
|
||||||
var Box = require('../box/box.jsx');
|
var Box = require('../box/box.jsx');
|
||||||
|
|
||||||
require('./news.scss');
|
require('./news.scss');
|
||||||
|
|
||||||
var defaultMessages = defineMessages({
|
|
||||||
scratchNews: {
|
|
||||||
id: 'news.scratchNews',
|
|
||||||
defaultMessage: 'Scratch News'
|
|
||||||
},
|
|
||||||
viewAll: {
|
|
||||||
id: 'general.viewAll',
|
|
||||||
defaultMessage: 'View All'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var News = React.createClass({
|
var News = React.createClass({
|
||||||
type: 'News',
|
type: 'News',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
@ -25,16 +11,19 @@ var News = React.createClass({
|
||||||
},
|
},
|
||||||
getDefaultProps: function () {
|
getDefaultProps: function () {
|
||||||
return {
|
return {
|
||||||
items: require('./news.json')
|
items: require('./news.json'),
|
||||||
|
messages: {
|
||||||
|
'general.viewAll': 'View All',
|
||||||
|
'news.scratchNews': 'Scratch News'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var formatMessage = this.props.intl.formatMessage;
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
className="news"
|
className="news"
|
||||||
title={formatMessage(defaultMessages.scratchNews)}
|
title={this.props.messages['news.scratchNews']}
|
||||||
moreTitle={formatMessage(defaultMessages.viewAll)}
|
moreTitle={this.props.messages['general.viewAll']}
|
||||||
moreHref="/discuss/5/">
|
moreHref="/discuss/5/">
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -55,4 +44,4 @@ var News = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = injectIntl(News);
|
module.exports = News;
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var ReactIntl = require('react-intl');
|
|
||||||
|
|
||||||
var injectIntl = ReactIntl.injectIntl;
|
|
||||||
var FormattedMessage = ReactIntl.FormattedMessage;
|
|
||||||
|
|
||||||
var Box = require('../box/box.jsx');
|
var Box = require('../box/box.jsx');
|
||||||
|
|
||||||
|
@ -13,10 +9,19 @@ var Welcome = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onDismiss: React.PropTypes.func
|
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 () {
|
render: function () {
|
||||||
var formatMessage = this.props.intl.formatMessage;
|
|
||||||
return (
|
return (
|
||||||
<Box title={formatMessage({id: 'welcome.welcomeToScratch', defaultMessage: 'Welcome to Scratch!'})}
|
<Box title={this.props.messages['welcome.welcomeToScratch']}
|
||||||
className="welcome"
|
className="welcome"
|
||||||
moreTitle="x"
|
moreTitle="x"
|
||||||
moreHref="#"
|
moreHref="#"
|
||||||
|
@ -29,9 +34,7 @@ var Welcome = React.createClass({
|
||||||
<div className="welcome-col blue">
|
<div className="welcome-col blue">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="/projects/editor/?tip_bar=getStarted">
|
<a href="/projects/editor/?tip_bar=getStarted">
|
||||||
<FormattedMessage
|
{this.props.messages['welcome.learn']}
|
||||||
id="welcome.learn"
|
|
||||||
defaultMessage="Learn how to make a project in Scratch" />
|
|
||||||
</a>
|
</a>
|
||||||
</h4>
|
</h4>
|
||||||
<a href="/projects/editor/?tip_bar=getStarted">
|
<a href="/projects/editor/?tip_bar=getStarted">
|
||||||
|
@ -41,9 +44,7 @@ var Welcome = React.createClass({
|
||||||
<div className="welcome-col green">
|
<div className="welcome-col green">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="/starter_projects/">
|
<a href="/starter_projects/">
|
||||||
<FormattedMessage
|
{this.props.messages['welcome.tryOut']}
|
||||||
id="welcome.tryOut"
|
|
||||||
defaultMessage="Try out starter projects" />
|
|
||||||
</a>
|
</a>
|
||||||
</h4>
|
</h4>
|
||||||
<a href="/starter_projects/">
|
<a href="/starter_projects/">
|
||||||
|
@ -53,9 +54,7 @@ var Welcome = React.createClass({
|
||||||
<div className="welcome-col pink">
|
<div className="welcome-col pink">
|
||||||
<h4>
|
<h4>
|
||||||
<a href="/studios/146521/">
|
<a href="/studios/146521/">
|
||||||
<FormattedMessage
|
{this.props.messages['welcome.connect']}
|
||||||
id="welcome.connect"
|
|
||||||
defaultMessage="Connect with other Scratchers" />
|
|
||||||
</a>
|
</a>
|
||||||
</h4>
|
</h4>
|
||||||
<a href="/studios/146521/">
|
<a href="/studios/146521/">
|
||||||
|
@ -67,4 +66,4 @@ var Welcome = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = injectIntl(Welcome);
|
module.exports = Welcome;
|
||||||
|
|
13
src/init.js
13
src/init.js
|
@ -1,8 +1,6 @@
|
||||||
var api = require('./mixins/api.jsx').api;
|
var api = require('./mixins/api.jsx').api;
|
||||||
var jar = require('./lib/jar');
|
var jar = require('./lib/jar');
|
||||||
|
|
||||||
var translations = require('../locales/translations.json');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
* Session
|
* Session
|
||||||
|
@ -72,17 +70,8 @@ var translations = require('../locales/translations.json');
|
||||||
obj = 'pt-br'; // default Portuguese users to Brazilian Portuguese due to our user base. Added in 2.2.5.
|
obj = 'pt-br'; // default Portuguese users to Brazilian Portuguese due to our user base. Added in 2.2.5.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
window._locale = updateLocale() || 'en';
|
window._locale = updateLocale();
|
||||||
window._translations = translations;
|
|
||||||
})();
|
})();
|
||||||
|
|
64
src/l10n.json
Normal file
64
src/l10n.json
Normal 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."
|
||||||
|
}
|
|
@ -3,12 +3,45 @@ var ReactDOM = require('react-dom');
|
||||||
var ReactIntl = require('./intl.jsx');
|
var ReactIntl = require('./intl.jsx');
|
||||||
var IntlProvider = ReactIntl.IntlProvider;
|
var IntlProvider = ReactIntl.IntlProvider;
|
||||||
|
|
||||||
|
var Navigation = require('../components/navigation/navigation.jsx');
|
||||||
|
var Footer = require('../components/footer/footer.jsx');
|
||||||
|
|
||||||
var render = function (jsx, element) {
|
var render = function (jsx, element) {
|
||||||
// Get locale and messages from global namespace (see "init.js")
|
// Get locale and messages from global namespace (see "init.js")
|
||||||
var locale = window._locale;
|
var locale = window._locale || 'en';
|
||||||
var messages = window._translations[locale];
|
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(
|
var component = ReactDOM.render(
|
||||||
<IntlProvider locale={locale} messages={messages}>
|
<IntlProvider locale={locale} messages={messages}>
|
||||||
{jsx}
|
{jsx}
|
||||||
|
@ -16,8 +49,6 @@ var render = function (jsx, element) {
|
||||||
element
|
element
|
||||||
);
|
);
|
||||||
|
|
||||||
// Provide list of rendered components
|
|
||||||
window._renderedComponents = window._renderedComponents || [];
|
|
||||||
window._renderedComponents.push(component);
|
window._renderedComponents.push(component);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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'));
|
|
|
@ -3,6 +3,7 @@ var FormattedHTMLMessage = require('react-intl').FormattedHTMLMessage;
|
||||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
var render = require('../../lib/render.jsx');
|
var render = require('../../lib/render.jsx');
|
||||||
|
|
||||||
|
require('../../main.scss');
|
||||||
require('./about.scss');
|
require('./about.scss');
|
||||||
|
|
||||||
var About = React.createClass({
|
var About = React.createClass({
|
||||||
|
|
26
src/views/about/l10n.json
Normal file
26
src/views/about/l10n.json
Normal 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'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 today’s 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'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."
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ var Carousel = require('../../components/carousel/carousel.jsx');
|
||||||
var Input = require('../../components/forms/input.jsx');
|
var Input = require('../../components/forms/input.jsx');
|
||||||
var Spinner = require('../../components/spinner/spinner.jsx');
|
var Spinner = require('../../components/spinner/spinner.jsx');
|
||||||
|
|
||||||
|
require('../../main.scss');
|
||||||
require('./components.scss');
|
require('./components.scss');
|
||||||
|
|
||||||
var Components = React.createClass({
|
var Components = React.createClass({
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var render = require('../../lib/render.jsx');
|
var render = require('../../lib/render.jsx');
|
||||||
|
|
||||||
|
require('../../main.scss');
|
||||||
require('./credits.scss');
|
require('./credits.scss');
|
||||||
|
|
||||||
var Credits = React.createClass({
|
var Credits = React.createClass({
|
||||||
|
|
|
@ -8,6 +8,7 @@ var Button = require('../../components/forms/button.jsx');
|
||||||
var Box = require('../../components/box/box.jsx');
|
var Box = require('../../components/box/box.jsx');
|
||||||
var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
|
var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
|
||||||
|
|
||||||
|
require('../../main.scss');
|
||||||
require('./hoc.scss');
|
require('./hoc.scss');
|
||||||
|
|
||||||
var Hoc = React.createClass({
|
var Hoc = React.createClass({
|
||||||
|
|
19
src/views/hoc/l10n.json
Normal file
19
src/views/hoc/l10n.json
Normal 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™ 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™\" 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? 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"
|
||||||
|
}
|
28
src/views/splash/l10n.json
Normal file
28
src/views/splash/l10n.json
Normal 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"
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ var Modal = require('../../components/modal/modal.jsx');
|
||||||
var News = require('../../components/news/news.jsx');
|
var News = require('../../components/news/news.jsx');
|
||||||
var Welcome = require('../../components/welcome/welcome.jsx');
|
var Welcome = require('../../components/welcome/welcome.jsx');
|
||||||
|
|
||||||
|
require('../../main.scss');
|
||||||
require('./splash.scss');
|
require('./splash.scss');
|
||||||
|
|
||||||
var Splash = injectIntl(React.createClass({
|
var Splash = injectIntl(React.createClass({
|
||||||
|
@ -318,6 +319,25 @@ var Splash = injectIntl(React.createClass({
|
||||||
var featured = this.renderHomepageRows();
|
var featured = this.renderHomepageRows();
|
||||||
var emailConfirmationStyle = {width: 500, height: 330, padding: 1};
|
var emailConfirmationStyle = {width: 500, height: 330, padding: 1};
|
||||||
var homepageCacheState = this.getHomepageRefreshStatus();
|
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 (
|
return (
|
||||||
<div className="splash">
|
<div className="splash">
|
||||||
{this.shouldShowEmailConfirmation() ? [
|
{this.shouldShowEmailConfirmation() ? [
|
||||||
|
@ -341,14 +361,16 @@ var Splash = injectIntl(React.createClass({
|
||||||
{this.state.session.user ? [
|
{this.state.session.user ? [
|
||||||
<div key="header" className="splash-header">
|
<div key="header" className="splash-header">
|
||||||
{this.shouldShowWelcome() ? [
|
{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} />
|
<Activity key="activity" items={this.state.activity} />
|
||||||
]}
|
]}
|
||||||
<News items={this.state.news} />
|
<News items={this.state.news} messages={messages} />
|
||||||
</div>
|
</div>
|
||||||
] : [
|
] : [
|
||||||
<Intro projectCount={this.state.projectCount} key="intro"/>
|
<Intro projectCount={this.state.projectCount} messages={messages} key="intro"/>
|
||||||
]}
|
]}
|
||||||
|
|
||||||
{featured}
|
{featured}
|
||||||
|
|
|
@ -3,7 +3,7 @@ var path = require('path');
|
||||||
var po2icu = require('po2icu');
|
var po2icu = require('po2icu');
|
||||||
var tap = require('tap');
|
var tap = require('tap');
|
||||||
|
|
||||||
var buildLocales = require('../../lib/locale-compare');
|
var buildLocales = require('../../bin/lib/locale-compare');
|
||||||
|
|
||||||
tap.test('buildLocalesFile', function (t) {
|
tap.test('buildLocalesFile', function (t) {
|
||||||
var md5map = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../fixtures/test_es_md5map.json'), 'utf8'));
|
var md5map = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../fixtures/test_es_md5map.json'), 'utf8'));
|
||||||
|
|
|
@ -2,7 +2,7 @@ var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var tap = require('tap');
|
var tap = require('tap');
|
||||||
|
|
||||||
var buildLocales = require('../../lib/locale-compare');
|
var buildLocales = require('../../bin/lib/locale-compare');
|
||||||
|
|
||||||
tap.test('buildLocalesFile', function (t) {
|
tap.test('buildLocalesFile', function (t) {
|
||||||
var actualMd5map = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../fixtures/test_es_md5map.json'), 'utf8'));
|
var actualMd5map = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../fixtures/test_es_md5map.json'), 'utf8'));
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
var tap = require('tap');
|
var tap = require('tap');
|
||||||
|
|
||||||
var buildLocales = require('../../lib/locale-compare');
|
var buildLocales = require('../../bin/lib/locale-compare');
|
||||||
|
|
||||||
tap.test('buildLocalesMergeTranslations', function (t) {
|
tap.test('buildLocalesMergeTranslations', function (t) {
|
||||||
var existingTranslations = {
|
var existingTranslations = {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
var tap = require('tap');
|
var tap = require('tap');
|
||||||
|
|
||||||
var buildLocales = require('../../lib/locale-compare');
|
var buildLocales = require('../../bin/lib/locale-compare');
|
||||||
|
|
||||||
tap.test('buildLocalesGetMD5', function (t) {
|
tap.test('buildLocalesGetMD5', function (t) {
|
||||||
var testString1 = 'are there bears here?';
|
var testString1 = 'are there bears here?';
|
||||||
|
|
|
@ -6,8 +6,7 @@ var routes = require('./server/routes.json');
|
||||||
|
|
||||||
// Prepare all entry points
|
// Prepare all entry points
|
||||||
var entry = {
|
var entry = {
|
||||||
init: './src/init.js',
|
init: './src/init.js'
|
||||||
main: './src/main.jsx'
|
|
||||||
};
|
};
|
||||||
routes.forEach(function (route) {
|
routes.forEach(function (route) {
|
||||||
entry[route.view] = './src/views/' + route.view + '/' + route.view + '.jsx';
|
entry[route.view] = './src/views/' + route.view + '/' + route.view + '.jsx';
|
||||||
|
@ -53,7 +52,8 @@ module.exports = {
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new CopyWebpackPlugin([
|
new CopyWebpackPlugin([
|
||||||
{from: 'static'}
|
{from: 'static'},
|
||||||
|
{from: 'intl', to: 'js'}
|
||||||
]),
|
]),
|
||||||
new webpack.optimize.UglifyJsPlugin({
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
compress: {
|
compress: {
|
||||||
|
|
Loading…
Reference in a new issue