From 4a9cdc7f4a176499bb4041bd8be010e49ba6e96a Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 21 Sep 2017 11:54:41 -0400 Subject: [PATCH] L10n packaging (#1) * Initial version of scratch-l10n package --- .babelrc | 7 +++ .eslintignore | 3 ++ .eslintrc.js | 6 +++ .gitignore | 12 +++++ gui/ar.json | 94 ++++++++++++++++++++++++++++++++++++++++ gui/de.json | 94 ++++++++++++++++++++++++++++++++++++++++ gui/en.json | 76 ++++++++++++++++++++++++++++++++ gui/es.json | 76 ++++++++++++++++++++++++++++++++ gui/fr.json | 18 -------- gui/he.json | 94 ++++++++++++++++++++++++++++++++++++++++ package.json | 37 ++++++++++++---- scripts/build-data.js | 80 ++++++++++++++++++++++++++++++++++ src/index.js | 17 ++++++++ src/supported-locales.js | 14 ++++++ webpack.config.js | 20 +++++++++ 15 files changed, 622 insertions(+), 26 deletions(-) create mode 100644 .babelrc create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 gui/ar.json create mode 100644 gui/de.json delete mode 100644 gui/fr.json create mode 100644 gui/he.json create mode 100755 scripts/build-data.js create mode 100644 src/index.js create mode 100644 src/supported-locales.js create mode 100644 webpack.config.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000000..a8cb44b4d6 --- /dev/null +++ b/.babelrc @@ -0,0 +1,7 @@ +{ + "plugins": [ + "transform-object-rest-spread" + ], + "presets": ["es2015"] +} + diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..740e4fdfba --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +node_modules/ +locales/* +dist/ diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..20f6a0ac53 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + extends: ['scratch', 'scratch/node'], + "plugins": [ + "json" + ] +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..6a7e95d487 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Mac OS +.DS_Store + +# NPM +/node_modules +npm-* + +# generated locale files +/locales + +# generated build files +/dist diff --git a/gui/ar.json b/gui/ar.json new file mode 100644 index 0000000000..a450be2722 --- /dev/null +++ b/gui/ar.json @@ -0,0 +1,94 @@ +{ + "action.addSound": { + "message": "أَضِف صَوْت", + "description": "Button to add a sound in the editor tab" + }, + "targetPane.addBackdrop": { + "message": "أَضِف خَلْفِيَّة", + "description": "Button to add a backdrop in the target pane" + }, + "action.addCostume": { + "message": "أَضِف تَخْصِيص", + "description": "Button to add a costume in the editor tab" + }, + "soundEditor.redo": { + "message": "إعادة", + "description": "Title of the button to redo" + }, + "soundEditor.play": { + "message": "تَشغيل", + "description": "Title of the button to start playing the sound" + }, + "soundEditor.sound": { + "message": "صَوْت", + "description": "Lable for the name of the sound" + }, + "soundEditor.save": { + "message": "حِفظ", + "description": "Title of the button to save trimmed sound" + }, + "action.addBackdrop": { + "message": "أَضِف خَلْفِيَّة", + "description": "Button to add a backdrop in the editor tab" + }, + "controls.turboMode": { + "message": "وَضْعِيَةْ التَرْبُو", + "description": "Label indicating turbo mode is active" + }, + "soundEditor.robot": { + "message": "روبوت", + "description": "Title of the button to apply the robot effect" + }, + "soundEditor.slower": { + "message": "أَبطأ", + "description": "Title of the button to apply the slower effect" + }, + "soundEditor.trim": { + "message": "قَصْ", + "description": "Title of the button to start trimminging the sound" + }, + "soundEditor.faster": { + "message": "أَسرع", + "description": "Title of the button to apply the faster effect" + }, + "soundEditor.echo": { + "message": "صَدى", + "description": "Title of the button to apply the echo effect" + }, + "soundEditor.reverse": { + "message": "مُتعاكس", + "description": "Title of the button to apply the reverse effect" + }, + "action.recordSound": { + "message": "سَجِّل صَوْت", + "description": "Button to record a sound in the editor tab" + }, + "soundEditor.softer": { + "message": "أخفض", + "description": "Title of the button to apply thr.softer effect" + }, + "soundEditor.stop": { + "message": "وَقفْ", + "description": "Title of the button to stop the sound" + }, + "stageSelector.backdrops": { + "message": "خَلْفِيَّة", + "description": "Label for the backdrops in the stage selector" + }, + "soundEditor.undo": { + "message": "تَراجع", + "description": "Title of the button to undo" + }, + "contextMenu.delete": { + "message": "حَذف", + "description": "Menu item to delete in the right click menu" + }, + "targetPane.addSprite": { + "message": "أَضِف كائن", + "description": "Button to add a sprite in the target pane" + }, + "soundEditor.louder": { + "message": "أعلى", + "description": "Title of the button to apply the louder effect" + } +} \ No newline at end of file diff --git a/gui/de.json b/gui/de.json new file mode 100644 index 0000000000..3bc5aa3f1d --- /dev/null +++ b/gui/de.json @@ -0,0 +1,94 @@ +{ + "action.addSound": { + "message": "Add Sound", + "description": "Button to add a sound in the editor tab" + }, + "targetPane.addBackdrop": { + "message": "Add Backdrop", + "description": "Button to add a backdrop in the target pane" + }, + "action.addCostume": { + "message": "Add Costume", + "description": "Button to add a costume in the editor tab" + }, + "soundEditor.redo": { + "message": "Redo", + "description": "Title of the button to redo" + }, + "soundEditor.play": { + "message": "Play", + "description": "Title of the button to start playing the sound" + }, + "soundEditor.sound": { + "message": "Sound", + "description": "Lable for the name of the sound" + }, + "soundEditor.save": { + "message": "Save", + "description": "Title of the button to save trimmed sound" + }, + "action.addBackdrop": { + "message": "Add Backdrop", + "description": "Button to add a backdrop in the editor tab" + }, + "controls.turboMode": { + "message": "Turbo Mode", + "description": "Label indicating turbo mode is active" + }, + "soundEditor.robot": { + "message": "Robot", + "description": "Title of the button to apply the robot effect" + }, + "soundEditor.slower": { + "message": "Slower", + "description": "Title of the button to apply the slower effect" + }, + "soundEditor.trim": { + "message": "Trim", + "description": "Title of the button to start trimminging the sound" + }, + "soundEditor.faster": { + "message": "Faster", + "description": "Title of the button to apply the faster effect" + }, + "soundEditor.echo": { + "message": "Echo", + "description": "Title of the button to apply the echo effect" + }, + "soundEditor.reverse": { + "message": "Reverse", + "description": "Title of the button to apply the reverse effect" + }, + "action.recordSound": { + "message": "Record Sound", + "description": "Button to record a sound in the editor tab" + }, + "soundEditor.softer": { + "message": "Softer", + "description": "Title of the button to apply thr.softer effect" + }, + "soundEditor.stop": { + "message": "Stop", + "description": "Title of the button to stop the sound" + }, + "stageSelector.backdrops": { + "message": "Backdrops", + "description": "Label for the backdrops in the stage selector" + }, + "soundEditor.undo": { + "message": "Undo", + "description": "Title of the button to undo" + }, + "contextMenu.delete": { + "message": "delete", + "description": "Menu item to delete in the right click menu" + }, + "targetPane.addSprite": { + "message": "Add Sprite", + "description": "Button to add a sprite in the target pane" + }, + "soundEditor.louder": { + "message": "Louder", + "description": "Title of the button to apply the louder effect" + } +} \ No newline at end of file diff --git a/gui/en.json b/gui/en.json index a1083fa5ef..3bc5aa3f1d 100644 --- a/gui/en.json +++ b/gui/en.json @@ -3,16 +3,92 @@ "message": "Add Sound", "description": "Button to add a sound in the editor tab" }, + "targetPane.addBackdrop": { + "message": "Add Backdrop", + "description": "Button to add a backdrop in the target pane" + }, "action.addCostume": { "message": "Add Costume", "description": "Button to add a costume in the editor tab" }, + "soundEditor.redo": { + "message": "Redo", + "description": "Title of the button to redo" + }, + "soundEditor.play": { + "message": "Play", + "description": "Title of the button to start playing the sound" + }, + "soundEditor.sound": { + "message": "Sound", + "description": "Lable for the name of the sound" + }, + "soundEditor.save": { + "message": "Save", + "description": "Title of the button to save trimmed sound" + }, "action.addBackdrop": { "message": "Add Backdrop", "description": "Button to add a backdrop in the editor tab" }, + "controls.turboMode": { + "message": "Turbo Mode", + "description": "Label indicating turbo mode is active" + }, + "soundEditor.robot": { + "message": "Robot", + "description": "Title of the button to apply the robot effect" + }, + "soundEditor.slower": { + "message": "Slower", + "description": "Title of the button to apply the slower effect" + }, + "soundEditor.trim": { + "message": "Trim", + "description": "Title of the button to start trimminging the sound" + }, + "soundEditor.faster": { + "message": "Faster", + "description": "Title of the button to apply the faster effect" + }, + "soundEditor.echo": { + "message": "Echo", + "description": "Title of the button to apply the echo effect" + }, + "soundEditor.reverse": { + "message": "Reverse", + "description": "Title of the button to apply the reverse effect" + }, "action.recordSound": { "message": "Record Sound", "description": "Button to record a sound in the editor tab" + }, + "soundEditor.softer": { + "message": "Softer", + "description": "Title of the button to apply thr.softer effect" + }, + "soundEditor.stop": { + "message": "Stop", + "description": "Title of the button to stop the sound" + }, + "stageSelector.backdrops": { + "message": "Backdrops", + "description": "Label for the backdrops in the stage selector" + }, + "soundEditor.undo": { + "message": "Undo", + "description": "Title of the button to undo" + }, + "contextMenu.delete": { + "message": "delete", + "description": "Menu item to delete in the right click menu" + }, + "targetPane.addSprite": { + "message": "Add Sprite", + "description": "Button to add a sprite in the target pane" + }, + "soundEditor.louder": { + "message": "Louder", + "description": "Title of the button to apply the louder effect" } } \ No newline at end of file diff --git a/gui/es.json b/gui/es.json index a1083fa5ef..3bc5aa3f1d 100644 --- a/gui/es.json +++ b/gui/es.json @@ -3,16 +3,92 @@ "message": "Add Sound", "description": "Button to add a sound in the editor tab" }, + "targetPane.addBackdrop": { + "message": "Add Backdrop", + "description": "Button to add a backdrop in the target pane" + }, "action.addCostume": { "message": "Add Costume", "description": "Button to add a costume in the editor tab" }, + "soundEditor.redo": { + "message": "Redo", + "description": "Title of the button to redo" + }, + "soundEditor.play": { + "message": "Play", + "description": "Title of the button to start playing the sound" + }, + "soundEditor.sound": { + "message": "Sound", + "description": "Lable for the name of the sound" + }, + "soundEditor.save": { + "message": "Save", + "description": "Title of the button to save trimmed sound" + }, "action.addBackdrop": { "message": "Add Backdrop", "description": "Button to add a backdrop in the editor tab" }, + "controls.turboMode": { + "message": "Turbo Mode", + "description": "Label indicating turbo mode is active" + }, + "soundEditor.robot": { + "message": "Robot", + "description": "Title of the button to apply the robot effect" + }, + "soundEditor.slower": { + "message": "Slower", + "description": "Title of the button to apply the slower effect" + }, + "soundEditor.trim": { + "message": "Trim", + "description": "Title of the button to start trimminging the sound" + }, + "soundEditor.faster": { + "message": "Faster", + "description": "Title of the button to apply the faster effect" + }, + "soundEditor.echo": { + "message": "Echo", + "description": "Title of the button to apply the echo effect" + }, + "soundEditor.reverse": { + "message": "Reverse", + "description": "Title of the button to apply the reverse effect" + }, "action.recordSound": { "message": "Record Sound", "description": "Button to record a sound in the editor tab" + }, + "soundEditor.softer": { + "message": "Softer", + "description": "Title of the button to apply thr.softer effect" + }, + "soundEditor.stop": { + "message": "Stop", + "description": "Title of the button to stop the sound" + }, + "stageSelector.backdrops": { + "message": "Backdrops", + "description": "Label for the backdrops in the stage selector" + }, + "soundEditor.undo": { + "message": "Undo", + "description": "Title of the button to undo" + }, + "contextMenu.delete": { + "message": "delete", + "description": "Menu item to delete in the right click menu" + }, + "targetPane.addSprite": { + "message": "Add Sprite", + "description": "Button to add a sprite in the target pane" + }, + "soundEditor.louder": { + "message": "Louder", + "description": "Title of the button to apply the louder effect" } } \ No newline at end of file diff --git a/gui/fr.json b/gui/fr.json deleted file mode 100644 index 4aaea3ab1b..0000000000 --- a/gui/fr.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "action.addSound": { - "message": "Ajouter Son", - "description": "Button to add a sound in the editor tab" - }, - "action.addCostume": { - "message": "Ajouter Costume", - "description": "Button to add a costume in the editor tab" - }, - "action.addBackdrop": { - "message": "Ajouter Arrière-plan", - "description": "Button to add a backdrop in the editor tab" - }, - "action.recordSound": { - "message": "Enregistrement du Son", - "description": "Button to record a sound in the editor tab" - } -} \ No newline at end of file diff --git a/gui/he.json b/gui/he.json new file mode 100644 index 0000000000..3bc5aa3f1d --- /dev/null +++ b/gui/he.json @@ -0,0 +1,94 @@ +{ + "action.addSound": { + "message": "Add Sound", + "description": "Button to add a sound in the editor tab" + }, + "targetPane.addBackdrop": { + "message": "Add Backdrop", + "description": "Button to add a backdrop in the target pane" + }, + "action.addCostume": { + "message": "Add Costume", + "description": "Button to add a costume in the editor tab" + }, + "soundEditor.redo": { + "message": "Redo", + "description": "Title of the button to redo" + }, + "soundEditor.play": { + "message": "Play", + "description": "Title of the button to start playing the sound" + }, + "soundEditor.sound": { + "message": "Sound", + "description": "Lable for the name of the sound" + }, + "soundEditor.save": { + "message": "Save", + "description": "Title of the button to save trimmed sound" + }, + "action.addBackdrop": { + "message": "Add Backdrop", + "description": "Button to add a backdrop in the editor tab" + }, + "controls.turboMode": { + "message": "Turbo Mode", + "description": "Label indicating turbo mode is active" + }, + "soundEditor.robot": { + "message": "Robot", + "description": "Title of the button to apply the robot effect" + }, + "soundEditor.slower": { + "message": "Slower", + "description": "Title of the button to apply the slower effect" + }, + "soundEditor.trim": { + "message": "Trim", + "description": "Title of the button to start trimminging the sound" + }, + "soundEditor.faster": { + "message": "Faster", + "description": "Title of the button to apply the faster effect" + }, + "soundEditor.echo": { + "message": "Echo", + "description": "Title of the button to apply the echo effect" + }, + "soundEditor.reverse": { + "message": "Reverse", + "description": "Title of the button to apply the reverse effect" + }, + "action.recordSound": { + "message": "Record Sound", + "description": "Button to record a sound in the editor tab" + }, + "soundEditor.softer": { + "message": "Softer", + "description": "Title of the button to apply thr.softer effect" + }, + "soundEditor.stop": { + "message": "Stop", + "description": "Title of the button to stop the sound" + }, + "stageSelector.backdrops": { + "message": "Backdrops", + "description": "Label for the backdrops in the stage selector" + }, + "soundEditor.undo": { + "message": "Undo", + "description": "Title of the button to undo" + }, + "contextMenu.delete": { + "message": "delete", + "description": "Menu item to delete in the right click menu" + }, + "targetPane.addSprite": { + "message": "Add Sprite", + "description": "Button to add a sprite in the target pane" + }, + "soundEditor.louder": { + "message": "Louder", + "description": "Title of the button to apply the louder effect" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 1ea0d00dfc..04cf7af279 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,40 @@ { - "name": "scratch-editor-translations", - "version": "0.5.0", - "description": "Translation file for the Scratch 3.0 Editor", - "main": "index.js", + "name": "scratch-l10n", + "version": "0.1.0", + "description": "Localization for the Scratch 3.0 components", + "main": "./dist/l10n.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build:data": "babel-node scripts/build-data", + "build": "npm run clean && npm run build:data && webpack --progress --colors --bail", + "clean": "rimraf ./dist ./locales && mkdirp dist locales", + "lint": "eslint . --ext .js,.json", + "test": "npm run lint && npm run build" }, "repository": { "type": "git", - "url": "git+https://github.com/LLK/scratch-editor-translations.git" + "url": "git+https://github.com/LLK/scratch-l10n.git" }, "author": "Massachusetts Institute of Technology", "license": "BSD-3-Clause", "bugs": { - "url": "https://github.com/LLK/scratch-editor-translations/issues" + "url": "https://github.com/LLK/scratch-l10n/issues" }, - "homepage": "https://github.com/LLK/scratch-editor-translations#readme" + "homepage": "https://github.com/LLK/scratch-l10n#readme", + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-core": "^6.26.0", + "babel-eslint": "^7.2.3", + "babel-loader": "^7.1.2", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "babel-preset-es2015": "^6.24.1", + "eslint": "^4.6.1", + "eslint-config-scratch": "^4.0.0", + "eslint-plugin-import": "^2.7.0", + "eslint-plugin-json": "1.2.0", + "lodash.defaultsdeep": "4.6.0", + "mkdirp": "^0.5.1", + "react-intl": "2.4.0", + "rimraf": "^2.6.2", + "webpack": "^3.5.6" + } } diff --git a/scripts/build-data.js b/scripts/build-data.js new file mode 100755 index 0000000000..b10e3b23e1 --- /dev/null +++ b/scripts/build-data.js @@ -0,0 +1,80 @@ +#!/usr/bin/env node + +/* +Generates locales/-msgs.js for each component (gui, etc) from the +current translation files for each language for that component + +Translation files are expected to be 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' + +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. + */ +import * as fs from 'fs'; +import * as path from 'path'; +import {sync as mkdirpSync} from 'mkdirp'; +import locales from '../src/supported-locales.js'; + +const MSGS_DIR = './locales/'; +let missingLocales = []; + +// GUI messages: +let component = 'gui'; +let messages = Object.keys(locales).reduce((collection, lang) => { + let langMessages = {}; + try { + let langData = JSON.parse( + fs.readFileSync(path.resolve(component, lang + '.json'), 'utf8') + ); + Object.keys(langData).forEach((id) => { + langMessages[id] = langData[id].message; + }); + collection[lang] = { + name: locales[lang], + messages: langMessages + }; + } catch (e) { + missingLocales.push(lang); + } + return collection; +}, {}); + +mkdirpSync(MSGS_DIR); +let data = + '// GENERATED FILE:\n' + + 'const ' + component + 'Msgs = ' + + JSON.stringify(messages, null, 2) + + '\nexports.locales = ' + component + 'Msgs;\n'; +fs.writeFileSync(MSGS_DIR + component + '-msgs.js', data); + +if (missingLocales.length > 0) { + process.stdout.write('missing locales: ' + missingLocales.toString()); + process.exit(1); +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000000..69f7bad587 --- /dev/null +++ b/src/index.js @@ -0,0 +1,17 @@ +import en from 'react-intl/locale-data/en'; +import ar from 'react-intl/locale-data/ar'; +import de from 'react-intl/locale-data/de'; +import es from 'react-intl/locale-data/es'; +import he from 'react-intl/locale-data/he'; + +import {locales} from '../locales/gui-msgs.js'; + +locales.en.localeData = en; +locales.ar.localeData = ar; +locales.de.localeData = de; +locales.es.localeData = es; +locales.he.localeData = he; + +export { + locales as default +}; diff --git a/src/supported-locales.js b/src/supported-locales.js new file mode 100644 index 0000000000..f93a549b26 --- /dev/null +++ b/src/supported-locales.js @@ -0,0 +1,14 @@ +/** + * Currently supported locales for the Scratch Project + * @type {Object} Key Value pairs of locale code: Language name written in the language + */ + +const locales = { + en: 'English', + ar: 'العربية', + de: 'Deutsch', + es: 'Español', + he: 'עִבְרִית' +}; + +export {locales as default}; diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000000..96d7600428 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,20 @@ +const path = require('path'); + +module.exports = { + devtool: 'cheap-module-source-map', + module: { + rules: [{ + test: /\.js$/, + loader: 'babel-loader', + include: path.resolve(__dirname, 'src') + }] + }, + entry: { + l10n: './src/index.js' + }, + output: { + path: path.resolve(__dirname, 'dist'), + filename: '[name].js', + libraryTarget: 'commonjs2' + } +};