diff --git a/.gitignore b/.gitignore
index 0eed05504..476cb5a20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,8 @@ npm-*
 # Locales
 /locales
 /intl
+/localizations
+
 
 # Elastic Beanstalk Files
 .elasticbeanstalk/*
diff --git a/.tx/config b/.tx/config
new file mode 100644
index 000000000..3a9d2df33
--- /dev/null
+++ b/.tx/config
@@ -0,0 +1,93 @@
+[main]
+host = https://www.transifex.com
+
+[scratch-website.explore-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/explore/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.general-l10njson]
+file_filter = localizations/general/<lang>.json
+source_file = src/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.wedo2-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/wedo2/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.cards-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/cards/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.teacherregistration-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/teacherregistration/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.dmca-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/dmca/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.jobs-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/jobs/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.faq-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/faq/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.about-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/about/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.teacher-faq-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/teachers/faq/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.developers-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/developers/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.things-to-try-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/thingstotry/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.guidelines-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/guidelines/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.educator-landing-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/teachers/landing/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
+[scratch-website.splash-l10njson]
+file_filter = localizations/${name}/<lang>.json
+source_file = src/views/splash/l10n.json
+source_lang = en
+type = KEYVALUEJSON
+
diff --git a/bin/import-pootle b/bin/import-pootle
new file mode 100755
index 000000000..9951ce0c1
--- /dev/null
+++ b/bin/import-pootle
@@ -0,0 +1,51 @@
+#!/usr/bin/env node
+ /*
+    Generate language json files corresponding to l10n files to import from
+    pootle into transifex.
+
+    For example, extract the strings corresponding to splash ids from pootle.
+    For each language, in localizations/splash create
+    fr.json =>
+    {
+        'splash.welcome' : 'Bienvenue',
+        ...
+    }
+    etc.
+ */
+
+var fs = require('fs');
+var path = require('path');
+var routes = require('../src/routes.json');
+var languages = require('../languages.json');
+var localeCompare = require('./lib/locale-compare');
+
+var outputDir = path.resolve(__dirname, '../localizations');
+try {
+    fs.accessSync(outputDir, fs.F_OK);
+} catch (err) {
+    // Doesn't exist - create it.
+    fs.mkdirSync(outputDir);
+}
+
+// general is a special case, do it first
+var l10n = path.resolve(__dirname, '../src/l10n.json');
+localeCompare.writeTranslations('general', l10n, languages);
+
+for (var v in routes) {
+    if (typeof routes[v].redirect !== 'undefined') {
+        continue;
+    }
+    var subdir = routes[v].view.split('/');
+    subdir.pop();
+    l10n = path.resolve(__dirname, '../src/views/' + subdir.join('/') + '/l10n.json');
+    var name = routes[v].name;
+    try {
+        // only import if there is an l10n file
+        fs.accessSync(l10n);
+    } catch (err) {
+        // skip views without l10n files
+        process.stdout.write(`Skipping ${name}, no l10n\n`);
+        continue;
+    }
+    localeCompare.writeTranslations(name, l10n, languages);
+}
diff --git a/bin/init-l10n-src b/bin/init-l10n-src
new file mode 100755
index 000000000..67c0ef6cc
--- /dev/null
+++ b/bin/init-l10n-src
@@ -0,0 +1,89 @@
+#!/usr/bin/env node
+
+/* Initialize transifex resources from src l10n files
+needs to run in the project root directory, where .tx folder is located
+add parameter 'execute' to run the tx command, otherwise transifex just
+shows what would run
+TBD: replace this with a node transifex module
+*/
+/*
+Don't use template strings (backticks) until scratch-www is switched over
+to ES6. Leaving template string versions as comments.
+ */
+
+// Unfortunately need to execute Synchronously, or tx set gets errors when
+// updating the .tx/config file
+var execSync = require('child_process').execSync;
+var fs = require('fs');
+var path = require('path');
+var routes = require('../src/routes.json');
+
+var cmd = '';
+var execute = '';
+
+// make sure .tx folder exists with config file
+try {
+    //
+    fs.accessSync(path.resolve(process.cwd() + '/.tx/config'));
+} catch (err) {
+    process.stdout.write('Run the script from the directory with .tx folder\n');
+    process.exit(1);
+}
+
+var args = process.argv.slice(2);
+if (args[0] === 'execute') {
+    process.stdout.write('executing tx initializtion\n');
+    execute = '--execute';
+} else {
+    process.stdout.write('Dry run: pass "execute" as a parameter to add --execute switch to commands\n');
+}
+
+
+// set up l10n resources for the scratch-website project as
+// [scratch-website.<view>_l10njson]
+// use 'general' for the root l10n.json
+
+// general is a special case, do that first
+// TODO: ES6
+// cmd = 'tx set --auto-local --source-lang en --type KEYVALUEJSON -r ' +
+//     'scratch-website.general-l10njson \'localizations/general/<lang>.json\' ' +
+//     `--source-file src/l10n.json ${execute}`;
+cmd = 'tx set --auto-local --source-lang en --type KEYVALUEJSON -r ' +
+    'scratch-website.general-l10njson \'localizations/general/<lang>.json\' ' +
+    '--source-file src/l10n.json ' + execute;
+process.stdout.write('Adding general l10n\n');
+execSync(cmd, {stdio:[0,1,2]});
+
+for (var v in routes) {
+    if (typeof routes[v].redirect !== 'undefined') {
+        continue;
+    }
+    var subdir = routes[v].view.split('/');
+    subdir.pop();
+    var l10n = 'src/views/' + subdir.join('/') + '/l10n.json';
+    var name = routes[v].name;
+    try {
+        // only Initialize if there is an l10n file
+        fs.accessSync(l10n);
+        // TODO: ES6
+        // var tx_resource = `scratch-website.${name}-l10njson`;
+        // cmd = `tx set --auto-local --source-lang en --type KEYVALUEJSON -r ${tx_resource}` +
+        // ` \'localizations/${name}/<lang>.json\' --source-file ${l10n} ${execute}`;
+        // process.stdout.write(`Adding ${name} l10n\n`);
+        var tx_resource = 'scratch-website.' + name +'-l10njson';
+        cmd = 'tx set --auto-local --source-lang en --type KEYVALUEJSON -r ' + tx_resource +
+            ' \'localizations/${name}/<lang>.json\' --source-file '+ l10n + ' ' + execute;
+        process.stdout.write('Adding ' + name + ' l10n\n');
+        execSync(cmd, {stdio:'inherit'});
+    } catch (err) {
+        // skip views without l10n files
+        // TODO: ES6
+        // process.stdout.write(`Skipping ${name}, no l10n\n`);
+        process.stdout.write('Skipping ' + name+ ', no l10n\n');
+    }
+}
+
+if (execute === '--execute') {
+    // push all the source files to transifex - force update
+    execSync('tx push -s -f --no-interactive', {stdio:'inherit'});
+}
diff --git a/bin/lib/locale-compare.js b/bin/lib/locale-compare.js
index ff0e4890a..d0e3fef44 100644
--- a/bin/lib/locale-compare.js
+++ b/bin/lib/locale-compare.js
@@ -62,6 +62,22 @@ Helpers.mergeNewTranslations = function (existingTranslations, newTranslations,
     return existingTranslations;
 };
 
+Helpers.mergeNewTranslationsWithoutDefaults = function (existingTranslations, newTranslations, icuTemplate, md5Map) {
+    for (var id in newTranslations) {
+        var md5 = Helpers.getMD5(id);
+        if (md5Map.hasOwnProperty(md5) && newTranslations[id].length > 0) {
+            md5Map[md5].forEach(function (msgId) {
+                existingTranslations[msgId] = newTranslations[id];
+            });
+        }
+    }
+
+    //Fill in empty string for any missing translations
+    for (var icuId in icuTemplate) {
+        if (!existingTranslations.hasOwnProperty(icuId)) existingTranslations[icuId] = '';
+    }
+    return existingTranslations;
+};
 /**
  * Converts a map of icu strings with react-intl id values into a map
  * with md5 hashes of the icu strings as keys and react-intl id values.
@@ -139,6 +155,65 @@ Helpers.getTranslationsForLanguage = function (lang, idsWithICU, md5WithIds, sep
     return translationsByView;
 };
 
+/**
+ * Grabs the translated strings from the po files for the given language and strings
+ * @param  {str}    lang       iso code of the language to use
+ * @param  {object} idsWithICU key: '<viewName>-<react-intl string id>'.
+ *                             value: english strings for translation
+ * @param  {object} md5WithIds key: md5 hash of the english strings for translation.
+ *                             value: '<viewName>-<react-intl string id>'
+ * @return {object}            translations – sub-objects by view containing:
+ *                                          key: '<react-intl string id>'
+ *                                          value: translated version of string
+ *                                                  empty if no translation present
+ */
+Helpers.getTranslationsForLanguageWithoutDefaults = function (lang, idsWithICU, md5WithIds, separator) {
+    var poUiDir = path.resolve(__dirname, '../../node_modules/scratchr2_translations/ui');
+    var jsFile = path.resolve(poUiDir, lang + '/LC_MESSAGES/djangojs.po');
+    var pyFile = path.resolve(poUiDir, lang + '/LC_MESSAGES/django.po');
+
+    var translations = {};
+    separator = separator || ':';
+
+    try {
+        fs.accessSync(jsFile, fs.R_OK);
+        var jsTranslations = po2icu.poFileToICUSync(lang, jsFile);
+        translations = Helpers.mergeNewTranslationsWithoutDefaults(translations,
+            jsTranslations, idsWithICU, md5WithIds);
+    } catch (err) {
+        if (err.code !== 'ENOENT') {
+            throw err;
+        }
+    }
+
+    try {
+        fs.accessSync(pyFile, fs.R_OK);
+        var pyTranslations = po2icu.poFileToICUSync(lang, pyFile);
+        translations = Helpers.mergeNewTranslationsWithoutDefaults(translations,
+            pyTranslations, idsWithICU, md5WithIds);
+    } catch (err) {
+        if (err.code !== 'ENOENT') {
+            throw err;
+        }
+    }
+
+    var translationsByView = {};
+    for (var id in translations) {
+        var ids = id.split(separator); // [viewName, stringId]
+        var viewName = ids[0];
+        var stringId = ids[1];
+
+        if (!translationsByView.hasOwnProperty(viewName)) {
+            translationsByView[viewName] = {};
+        }
+        if (!translationsByView[viewName].hasOwnProperty(lang)) {
+            translationsByView[viewName][lang] = {};
+        }
+        translationsByView[viewName][lang][stringId] = translations[id];
+    }
+    return translationsByView;
+};
+
 Helpers.writeTranslationsToJS = function (outputDir, viewName, translationObject) {
     var objectString = JSON.stringify(translationObject);
     var fileString = 'window._messages = ' + objectString + ';';
@@ -169,4 +244,30 @@ Helpers.icuToIdMap = function (viewName, ids, separator) {
     return icuToIds;
 };
 
+Helpers.writeTranslations = function (name, l10n, languages) {
+    var ids = require(l10n);
+    var idsWithICU = Helpers.idToICUMap(name, ids);
+    var icuWithIds = Helpers.icuToIdMap(name, ids);
+    var md5WithIds = Helpers.getMD5Map(icuWithIds);
+    var isoCodes = Object.keys(languages);
+    var outputDir = path.resolve(__dirname, '../../localizations/', name);
+    try {
+        fs.accessSync(outputDir, fs.F_OK);
+    } catch (err) {
+        // Doesn't exist - create it.
+        fs.mkdirSync(outputDir);
+    }
+    process.stdout.write(`Writing translations to ${outputDir}\n`);
+
+    for (var isoCode in isoCodes) {
+        if (isoCodes[isoCode] !== 'en'){
+            var translations = Helpers.getTranslationsForLanguageWithoutDefaults(
+                isoCodes[isoCode], idsWithICU, md5WithIds);
+            var fileString = JSON.stringify(translations[name][isoCodes[isoCode]]);
+            fs.writeFileSync(outputDir + '/' + isoCodes[isoCode] + '.json', fileString);
+        }
+    }
+
+};
+
 module.exports = Helpers;