diff --git a/.tx/config b/.tx/config
new file mode 100644
index 00000000..157b3167
--- /dev/null
+++ b/.tx/config
@@ -0,0 +1,8 @@
+[main]
+host = https://www.transifex.com
+
+[experimental-scratch.scratch-paint]
+file_filter = translations/<lang>.json
+source_file = translations/en.json
+source_lang = en
+type = CHROME
diff --git a/package.json b/package.json
index 05d81460..497c8818 100644
--- a/package.json
+++ b/package.json
@@ -4,13 +4,12 @@
   "description": "Graphical User Interface for the Scratch 3.0 paint editor, which is used to make and edit sprites for use in projects.",
   "main": "./dist/scratch-paint.js",
   "scripts": {
-    "build": "npm run clean && npm run i18n:msgs && webpack --progress --colors --bail",
+    "build": "npm run clean && webpack --progress --colors --bail",
     "clean": "rimraf ./dist && mkdirp dist && rimraf ./playground && mkdirp playground",
     "deploy": "touch playground/.nojekyll && gh-pages -t -d playground -m \"Build for $(git log --pretty=format:%H -n1)\"",
-    "i18n:msgs": "node ./scripts/generate-locale-messages.js",
     "i18n:src": "babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/",
     "lint": "eslint . --ext .js,.jsx",
-    "start": "npm run i18n:msgs && webpack-dev-server",
+    "start": "webpack-dev-server",
     "test": "npm run lint && npm run unit && NODE_ENV=production npm run build",
     "unit": "jest",
     "watch": "webpack --progress --colors --watch"
@@ -74,6 +73,7 @@
     "redux-throttle": "0.1.1",
     "regenerator-runtime": "^0.10.5",
     "rimraf": "^2.6.1",
+    "scratch-l10n": "^2.0.0",
     "style-loader": "^0.18.0",
     "tap": "^10.2.0",
     "webpack": "^3.5.4",
diff --git a/scripts/generate-locale-messages.js b/scripts/generate-locale-messages.js
deleted file mode 100755
index 44a1c581..00000000
--- a/scripts/generate-locale-messages.js
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env node
-
-/*
-Generates locale/messages.json from current translastion files
-
-Translations are expected to be in the ./translations directory.
-Translation files are in Chrome i18n json format:
-'''
-{
-    "message.id": {
-        "message": "The translated text",
-        "description": "Tips for translators"
-    },
-    ...
-}
-'''
-They are named by locale, for example: 'fr.json' or 'zh-cn.json'
-
-Current languages supported are listed in ../src/languages.json
-
-Converts the collection of translation files to a single set of messages.
-Example output:
-'''
-{
-  "en": {
-    "action.addBackdrop": "Add Backdrop",
-    "action.addCostume": "Add Costume",
-    "action.recordSound": "Record Sound",
-    "action.addSound": "Add Sound"
-  },
-  "fr": {
-    "action.addSound": "Ajouter Son",
-    "action.addCostume": "Ajouter Costume",
-    "action.addBackdrop": "Ajouter Arrière-plan",
-    "action.recordSound": "Enregistrement du Son"
-  }
-}
-'''
-
-Missing locales are ignored, react-intl will use the default messages for them.
- */
-const fs = require('fs');
-const path = require('path');
-const mkdirp = require('mkdirp');
-
-const locales = ['en'];
-const LANG_DIR = './translations/';
-const MSGS_DIR = './locale/';
-
-let messages = locales.reduce((collection, lang) => {
-    let langMessages = {};
-    try {
-        let langData = JSON.parse(
-            fs.readFileSync(path.resolve(LANG_DIR, lang + '.json'), 'utf8')
-        );
-        Object.keys(langData).forEach((id) => {
-            langMessages[id] = langData[id].message;
-        });
-        collection[lang] = langMessages;
-    } catch (e) {
-        process.stdout.write(lang + ' translation file missing, will use defaults.\n');
-    }
-    return collection;
-}, {});
-
-mkdirp.sync(MSGS_DIR);
-fs.writeFileSync(MSGS_DIR + 'messages.json', JSON.stringify(messages, null, 2));
diff --git a/src/locale.js b/src/locale.js
deleted file mode 100644
index fb1e72e9..00000000
--- a/src/locale.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import localeDataEn from 'react-intl/locale-data/en';
-
-import messages from '../locale/messages.json'; // eslint-disable-line import/no-unresolved
-
-export default {
-    en: {
-        name: 'English',
-        localeData: localeDataEn,
-        messages: messages.en
-    }
-};
diff --git a/src/playground/reducers/intl.js b/src/playground/reducers/intl.js
index a9d43eda..62525cee 100644
--- a/src/playground/reducers/intl.js
+++ b/src/playground/reducers/intl.js
@@ -2,24 +2,25 @@ import {addLocaleData} from 'react-intl';
 import {updateIntl as superUpdateIntl} from 'react-intl-redux';
 import {IntlProvider, intlReducer} from 'react-intl-redux';
 
-import locales from '../../locale.js';
+import localeData from 'scratch-l10n';
+import paintMessages from 'scratch-l10n/locales/paint-msgs';
 
-Object.keys(locales).forEach(locale => {
+Object.keys(localeData).forEach(locale => {
     // TODO: will need to handle locales not in the default intl - see www/custom-locales
-    addLocaleData(locales[locale].localeData);
+    addLocaleData(localeData[locale].localeData);
 });
 
 const intlInitialState = {
     intl: {
         defaultLocale: 'en',
         locale: 'en',
-        messages: locales.en.messages
+        messages: paintMessages.messages.en.messages
     }
 };
 
 const updateIntl = locale => superUpdateIntl({
     locale: locale,
-    messages: locales[locale].messages || locales.en.messages
+    messages: paintMessages.messages[locale].messages || paintMessages.messages.en.messages
 });
 
 export {