Merge pull request #551 from chrisgarrity/feature/gh511-language-selector
Add language selection widget to menubar
This commit is contained in:
commit
c68653a38e
13 files changed
+198
-44
No files matched your search
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"plugins": [
|
||||
"syntax-dynamic-import",
|
||||
"transform-object-rest-spread",
|
||||
["react-intl", {
|
||||
"messagesDir": "./translations/messages/"
|
||||
|
||||
+2
-1
@@ -10,7 +10,7 @@
|
||||
"i18n:msgs": "./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": "webpack-dev-server",
|
||||
"start": "npm run i18n:msgs && webpack-dev-server",
|
||||
"test": "npm run lint && npm run build",
|
||||
"watch": "webpack --progress --colors --watch"
|
||||
},
|
||||
@@ -32,6 +32,7 @@
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-plugin-react-intl": "2.3.1",
|
||||
"babel-plugin-syntax-dynamic-import": "6.18.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.22.0",
|
||||
"babel-preset-es2015": "^6.22.0",
|
||||
"babel-preset-react": "^6.22.0",
|
||||
|
||||
@@ -16,7 +16,7 @@ Translation files are in Chrome i18n json format:
|
||||
'''
|
||||
They are named by locale, for example: 'fr.json' or 'zh-cn.json'
|
||||
|
||||
Current languages supported are listed in ../languages.json
|
||||
Current languages supported are listed in ../src/languages.json
|
||||
|
||||
Converts the collection of translation files to a single set of messages.
|
||||
Example output:
|
||||
@@ -42,7 +42,7 @@ 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 languages = require('../languages.json');
|
||||
const languages = require('../src/languages.json');
|
||||
|
||||
const LANG_DIR = './translations/';
|
||||
const MSGS_DIR = './locale/';
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 300 300" enable-background="new 0 0 300 300" xml:space="preserve">
|
||||
<g>
|
||||
<polygon fill="#999999" points="148.7,59.1 68,30.6 68,206.1 148.7,180 "/>
|
||||
<polygon fill="#FFFFFF" points="146.7,59 230.5,30.5 230.5,206 146.7,180 "/>
|
||||
<polygon fill="#FFFFFF" points="47.1,236.9 146.7,203.7 146.7,58.9 47.1,92.2 "/>
|
||||
<polygon fill="#999999" points="192.1,239.1 206.2,262.3 213.7,240.7 "/>
|
||||
<g>
|
||||
<path fill="#999999" d="M81,108.8c-0.5-0.5,0.7,4.2,2.4,6c3,3,5.4,3.4,6.6,3.5c2.8,0.1,6.2-0.7,8.2-1.5c2-0.8,5.4-2.6,6.7-5.2
|
||||
c0.3-0.5,1-1.5,0.6-3.7c-0.4-1.7-1.5-2.4-2.8-2.3c-1.4,0.1-5.5,1.2-7.4,1.8c-2,0.6-6.1,1.8-7.9,2.2C85.5,110,81.6,109.4,81,108.8z
|
||||
"/>
|
||||
<path fill="#999999" d="M131.2,165.8c-0.8-0.3-17-7-19.3-8.1c-1.9-0.9-6.5-2.9-8.7-3.7c6.1-9.4,10-16.6,10.5-17.7
|
||||
c1-2,7.4-14.7,7.6-15.4c0.1-0.8,0.3-3.7,0.2-4.4c-0.1-0.7-2.5,0.7-5.7,1.7c-3.2,1.1-9.4,5.1-11.7,5.6c-2.4,0.5-10,3.4-13.9,4.7
|
||||
c-3.9,1.3-11.3,3.5-14.3,4.4c-3,0.8-5.7,0.9-7.4,1.4c0,0,0.2,2.4,0.7,3.1c0.4,0.7,2.1,2.5,3.9,2.9c1.9,0.5,5,0.3,6.4,0
|
||||
c1.4-0.3,3.8-1.5,4.2-2c0.3-0.5-0.2-2.2,0.4-2.6c0.6-0.5,8-2.2,10.8-3.1c2.8-0.9,13.5-4.6,15-4.4c-0.5,1.5-9.1,18.6-11.9,23.7
|
||||
c-2.8,5.1-18.9,27.5-22.3,31.4c-2.6,3-8.9,10.7-11.1,12.4c0.6,0.2,4.5-0.2,5.2-0.6c4.4-2.7,11.8-11.9,14.2-14.7
|
||||
c7.1-8.3,13.3-17,18.2-24.5h0c1,0.4,8.7,6.7,10.8,8.1c2,1.4,10,5.9,11.8,6.6c1.7,0.7,8.4,3.8,8.7,2.8
|
||||
C133.5,172.2,132,166.1,131.2,165.8z"/>
|
||||
</g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#999999" d="M93.7,250.4c1.6,1,3,1.7,4.7,2.5c3.3,1.6,7,3.4,10.5,4.7
|
||||
c4.8,1.8,9.7,3.3,14.5,4.4c2.7,0.6,5.6,1.1,8.5,1.6c0.3,0,8,1,9.5,1h7.8c3-0.3,5.9-0.4,8.9-0.9c2.4-0.3,5.1-0.8,7.7-1.4
|
||||
c1.9-0.4,3.9-0.9,5.8-1.5c1.8-0.5,3.9-1.2,5.9-1.9c1.3-0.4,2.7-1,4.1-1.6c1.1-0.5,2.5-1.1,3.8-1.6c1.6-0.7,3.4-1.6,5.1-2.5
|
||||
c1.4-0.7,2.9-1.6,4.4-2.4c1.1-0.6,3.7-2.6,5.1-2.6c1.6,0,2.6,1.4,2.6,2.6c0,2.5-3.4,3.3-4.9,4.4c-1.6,1.1-3.6,2-5.4,2.9
|
||||
c-3.5,1.8-7,3.4-10.4,4.7c-4.4,1.6-9.3,3.2-13.6,4.2c-1.6,0.3-3.3,0.8-4.9,1c-0.9,0.2-9.9,1.6-12.4,1.6h-11.4c-3-0.3-6.2-0.6-9.3-1
|
||||
c-2.7-0.4-5.5-1-8.2-1.6c-2.1-0.4-4.3-1-6.3-1.6c-3.5-1-6.8-2.2-10.1-3.5c-6-2.2-12.2-5.2-18.1-9.1c-1-0.7-1.1-1.4-1.1-2.2
|
||||
c0-1.3,1-2.5,2.5-2.5C90.4,248.2,93.2,250.1,93.7,250.4z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#999999" d="M144,201.7l-94.3,31.4v-139L144,62.7V201.7z M149.3,58.6v145.3
|
||||
c-0.1,0.4-0.3,0.9-0.6,1.3c-0.2,0.3-0.5,0.6-0.8,0.7c-2.2,0.9-99.5,33.6-100.8,33.6c-1,0-2-0.7-2.5-1.8c0-0.1-0.1-0.2-0.1-0.3V91.9
|
||||
c0.2-0.4,0.3-1,0.6-1.4c0.7-1,1.9-1.1,2.7-1.4c1.5-0.5,97.4-32.7,98.8-32.7C147.4,56.4,149.3,57.1,149.3,58.6z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#999999" d="M227.8,202.4L149.4,178V60.9l78.4-26.6V202.4z M233.1,30.8v174.9
|
||||
c-0.1,2-1.5,2.9-2.8,2.9c-1.1,0-9.3-2.8-10.6-3.2c-10.9-3.4-21.9-6.7-32.7-10.1c-2.4-0.8-4.9-1.6-7.3-2.3c-2.1-0.6-4.3-1.3-6.4-2
|
||||
c-9.3-2.9-18.7-5.7-27.9-8.8c-0.3-0.1-1.2-1.3-1.2-1.6V58.4c0.2-0.4,0.3-1,0.8-1.3c0.7-0.8,30.4-10.6,42-14.5
|
||||
c3.1-1.1,42.1-14.5,43.3-14.5C231.9,28,233.1,29.1,233.1,30.8z"/>
|
||||
<polygon fill="#999999" points="253,239.1 148.2,205.7 148.7,60.3 253,93.5 "/>
|
||||
<g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M200.2,125.2l-10.3,24.9l18.9,5.7L200.2,125.2z M194,101.8
|
||||
l13.5,4.1l24.6,88.6l-13.9-4.2l-5-18.2l-28.7-8.7l-6.2,14.8l-13.9-4.2L194,101.8z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,23 @@
|
||||
@import "../../css/units.css";
|
||||
|
||||
.group {
|
||||
display: inline-flex;
|
||||
flex-direction: row; /* makes columns, for each label/form group */
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.language-icon {
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.language-select {
|
||||
width: 100%;
|
||||
height: 1.85rem;
|
||||
border: 1px solid #4c97ff;
|
||||
user-select: none;
|
||||
outline: none;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import Box from '../box/box.jsx';
|
||||
import languages from '../../languages.json';
|
||||
import languageIcon from './language-icon.svg';
|
||||
import styles from './language-selector.css';
|
||||
|
||||
const LanguageSelector = ({
|
||||
currentLocale,
|
||||
onChange,
|
||||
...props
|
||||
}) => (
|
||||
<Box {...props}>
|
||||
<div className={styles.group}>
|
||||
<img
|
||||
className={styles.languageIcon}
|
||||
src={languageIcon}
|
||||
/>
|
||||
<select
|
||||
className={styles.languageSelect}
|
||||
value={currentLocale}
|
||||
onChange={onChange}
|
||||
>
|
||||
{Object.keys(languages).map(locale => (
|
||||
<option
|
||||
key={locale}
|
||||
value={locale}
|
||||
>
|
||||
{languages[locale]}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
|
||||
|
||||
LanguageSelector.propTypes = {
|
||||
currentLocale: PropTypes.string,
|
||||
onChange: PropTypes.func
|
||||
};
|
||||
|
||||
module.exports = LanguageSelector;
|
||||
@@ -4,6 +4,7 @@ import React from 'react';
|
||||
import Box from '../box/box.jsx';
|
||||
import LoadButton from '../../containers/load-button.jsx';
|
||||
import SaveButton from '../../containers/save-button.jsx';
|
||||
import LanguageSelector from '../../containers/language-selector.jsx';
|
||||
|
||||
import styles from './menu-bar.css';
|
||||
import scratchLogo from './scratch-logo.svg';
|
||||
@@ -23,6 +24,7 @@ const MenuBar = function MenuBar () {
|
||||
</div>
|
||||
<SaveButton className={styles.menuItem} />
|
||||
<LoadButton className={styles.menuItem} />
|
||||
<LanguageSelector className={styles.menuItem} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import {connect} from 'react-redux';
|
||||
import {updateIntl} from '../reducers/intl.js';
|
||||
|
||||
import LanguageSelectorComponent from '../components/language-selector/language-selector.jsx';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
currentLocale: state.intl.locale
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onChange: e => {
|
||||
e.preventDefault();
|
||||
dispatch(updateIntl(e.target.value));
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(LanguageSelectorComponent);
|
||||
+8
-10
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {Provider} from 'react-redux';
|
||||
import {createStore, applyMiddleware} from 'redux';
|
||||
import {createStore, applyMiddleware, compose} from 'redux';
|
||||
import throttle from 'redux-throttle';
|
||||
import {intlInitialState, IntlProvider} from './lib/intl.jsx';
|
||||
import {intlInitialState, IntlProvider} from './reducers/intl.js';
|
||||
|
||||
import GUI from './containers/gui.jsx';
|
||||
import log from './lib/log';
|
||||
@@ -61,16 +61,14 @@ class App extends React.Component {
|
||||
const appTarget = document.createElement('div');
|
||||
appTarget.className = styles.app;
|
||||
document.body.appendChild(appTarget);
|
||||
const store = applyMiddleware(
|
||||
throttle(300, {leading: true, trailing: true})
|
||||
)(createStore)(
|
||||
reducer,
|
||||
Object.assign(
|
||||
{},
|
||||
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
|
||||
intlInitialState
|
||||
|
||||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
const enhancer = composeEnhancers(
|
||||
applyMiddleware(
|
||||
throttle(300, {leading: true, trailing: true})
|
||||
)
|
||||
);
|
||||
const store = createStore(reducer, intlInitialState, enhancer);
|
||||
|
||||
ReactDOM.render((
|
||||
<Provider store={store}>
|
||||
|
||||
File renamed without changes.
@@ -1,19 +0,0 @@
|
||||
import {addLocaleData} from 'react-intl';
|
||||
import languages from '../../languages.json';
|
||||
import messages from '../../locale/messages.json';
|
||||
|
||||
export {IntlProvider, intlReducer} from 'react-intl-redux';
|
||||
|
||||
Object.keys(languages).forEach(locale => {
|
||||
// TODO: will need to handle locales not in the default intl - see www/custom-locales
|
||||
const data = require(`react-intl/locale-data/${locale}`);
|
||||
addLocaleData(data);
|
||||
});
|
||||
|
||||
export const intlInitialState = {
|
||||
intl: {
|
||||
defaultLocale: 'en',
|
||||
locale: 'en',
|
||||
messages: messages.en
|
||||
}
|
||||
};
|
||||
+23
-3
@@ -1,5 +1,25 @@
|
||||
import {addLocaleData} from 'react-intl';
|
||||
import {updateIntl as superUpdateIntl} from 'react-intl-redux';
|
||||
import {messages} from '../../locale/messages.json';
|
||||
import languages from '../languages.json';
|
||||
import messages from '../../locale/messages.json';
|
||||
import {IntlProvider, intlReducer} from 'react-intl-redux';
|
||||
|
||||
export {intlReducer as default} from 'react-intl-redux';
|
||||
export const updateIntl = locale => superUpdateIntl(locale, messages[locale] || messages.en);
|
||||
Object.keys(languages).forEach(locale => {
|
||||
// TODO: will need to handle locales not in the default intl - see www/custom-locales
|
||||
import(`react-intl/locale-data/${locale}`).then(data => addLocaleData(data));
|
||||
});
|
||||
|
||||
const intlInitialState = {
|
||||
intl: {
|
||||
defaultLocale: 'en',
|
||||
locale: 'en',
|
||||
messages: messages.en
|
||||
}
|
||||
};
|
||||
|
||||
const updateIntl = locale => superUpdateIntl({
|
||||
locale: locale,
|
||||
messages: messages[locale] || messages.en
|
||||
});
|
||||
|
||||
export {intlReducer as default, IntlProvider, intlInitialState, updateIntl};
|
||||
+1
-9
@@ -33,15 +33,7 @@ module.exports = {
|
||||
rules: [{
|
||||
test: /\.jsx?$/,
|
||||
loader: 'babel-loader',
|
||||
include: path.resolve(__dirname, 'src'),
|
||||
options: {
|
||||
plugins: [
|
||||
'transform-object-rest-spread',
|
||||
['react-intl', {
|
||||
messagesDir: './translations/messages/'
|
||||
}]],
|
||||
presets: ['es2015', 'react']
|
||||
}
|
||||
include: path.resolve(__dirname, 'src')
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
|
||||
Reference in new issue
Block a user