Skip to content

Commit

Permalink
Merge pull request #54 from mircobe87/issue-32
Browse files Browse the repository at this point in the history
#32 multilanguage support
  • Loading branch information
mbarto committed Aug 26, 2015
2 parents 9ce80f5 + c6da86c commit a2ce0bf
Show file tree
Hide file tree
Showing 15 changed files with 626 additions and 25 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"proj4": "~2.3.6",
"react": "^0.13.3",
"react-bootstrap": "^0.24.3",
"react-intl": "^1.2.0",
"url": "~0.10.3"
},
"scripts": {
Expand Down
23 changes: 23 additions & 0 deletions web/client/actions/I18NActions.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright 2015, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
var assign = require('object-assign');
var keymirror = require('keymirror');

var Dispatcher = require('../dispatchers/Dispatcher');

var I18NActions = {
launch(actionType, args) {
Dispatcher.dispatch(assign({type: actionType}, args));
}
};

assign(I18NActions, keymirror({
CHANGE_LANG: null
}));

module.exports = I18NActions;
40 changes: 40 additions & 0 deletions web/client/actions/__tests__/I18NActions-test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright 2015, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
var expect = require('expect');
var assign = require('object-assign');
var Dispatcher = require('../../dispatchers/Dispatcher');

describe('This test for I18NActions', () => {
var I18NActions;

beforeEach((done) => {
I18NActions = require('../I18NActions');
setTimeout(done);
});

afterEach((done) => {
var name = require.resolve('../I18NActions');
delete require.cache[name];
setTimeout(done);
});

it('checks launch(actionType, args)', () => {
const aType = "test";
const aArgs = {
k0: "v0",
k1: "v1"
};
const testArgs = assign({type: aType}, aArgs);
const spy = expect.spyOn(Dispatcher, 'dispatch');
I18NActions.launch(aType, aArgs);

expect(spy.calls.length).toBe(1);
expect(spy.calls[0].context).toBe(Dispatcher);
expect(spy.calls[0].arguments).toEqual([ testArgs ]);
});
});
8 changes: 8 additions & 0 deletions web/client/components/I18N/I18N.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright 2015, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
module.exports.Message = require('./Message');
49 changes: 49 additions & 0 deletions web/client/components/I18N/Message.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright 2015, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
var React = require('react');
var ReactIntl = require('react-intl');
var FormattedMessage = ReactIntl.FormattedMessage;

var I18NStore = require('../../stores/I18NStore');

var Message = React.createClass({
propTypes: {
msgId: React.PropTypes.string.isRequired,
msgParams: React.PropTypes.object
},
getInitialState() {
const currentLocale = I18NStore.getCurrentLocale();
const msg = I18NStore.getMsgById(this.props.msgId);
return {
locales: currentLocale,
msg: msg
};
},
// it makes this component reactive when a new language is loaded in
// language store.
componentDidMount() {
I18NStore.register(I18NStore.Event.LANG_CHANGED, this.onLangChanged);
},
componentWillUnmount() {
I18NStore.unregister(I18NStore.Event.LANG_CHANGED, this.onLangChanged);
},
// it updates the state of this component when the language store loads a new one.
onLangChanged() {
const currentLocale = I18NStore.getCurrentLocale();
const msg = I18NStore.getMsgById(this.props.msgId);
this.setState({
locales: currentLocale,
msg: msg
});
},
render() {
return <FormattedMessage locales={this.state.locales} message={this.state.msg} {...this.props.msgParams}/>;
}
});

module.exports = Message;
53 changes: 53 additions & 0 deletions web/client/components/I18N/__tests__/I18N.Message-test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Copyright 2015, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
var expect = require('expect');

var React = require('react/addons');
var I18NStore = require('../../../stores/I18NStore');
var I18N = require('../I18N');
var I18NActions = require('../../../actions/I18NActions');

var ita = require('../../../stores/data.it-IT');
var eng = require('../../../stores/data.en-US');

describe('This test for I18N.Message', () => {
afterEach((done) => {
React.unmountComponentAtNode(document.body);
document.body.innerHTML = '';
setTimeout(done);
});

it('checks if the component reacts at I18NStore edits', () => {
var testMsg;
var currentData;
const msgId = "aboutLbl";
const data = {
"en-US": eng,
"it-IT": ita
};
currentData = data[I18NStore.getCurrentLocale()];
testMsg = currentData.messages[msgId];

const cmp = React.render(<I18N.Message msgId={msgId}/>, document.body);
expect(cmp).toExist();

const cmpDom = React.findDOMNode(cmp);
expect(cmpDom).toExist();
expect(cmpDom.innerHTML).toBe(testMsg);

const nextLocale = I18NStore.getCurrentLocale() === "it-It" ? "en-US" : "it-IT";
I18NStore._emulate_dispatcher({
locale: nextLocale,
type: I18NActions.CHANGE_LANG
});

currentData = data[I18NStore.getCurrentLocale()];
testMsg = currentData.messages[msgId];
expect(cmpDom.innerHTML).toBe(testMsg);
});
});
49 changes: 49 additions & 0 deletions web/client/components/LangSelector/LangSelector.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

/**
* Copyright 2015, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
var React = require('react');
var BootstrapReact = require('react-bootstrap');
var Input = BootstrapReact.Input;

var I18NStore = require('../../stores/I18NStore');
var I18NActions = require('../../actions/I18NActions');

var LangSelector = React.createClass({
propTypes: {
locales: React.PropTypes.object
},
getDefaultProps() {
return {
locales: I18NStore.getSupportedLocales()
};
},
render() {
var val;
var label;
var list = [];
for (let lang in this.props.locales) {
if (this.props.locales.hasOwnProperty(lang)) {
val = this.props.locales[lang];
label = lang;
list.push(<option value={val} key={val}>{label}</option>);
}
}
return (
<Input type="select" bsSize="small" onChange={this.launchNewLangAction}>
{list}
</Input>
);
},
launchNewLangAction() {
var element = React.findDOMNode(this);
var selectNode = element.getElementsByTagName('select').item(0);
I18NActions.launch(I18NActions.CHANGE_LANG, {locale: selectNode.value});
}
});

module.exports = LangSelector;
59 changes: 59 additions & 0 deletions web/client/components/LangSelector/__tests__/LangSelector-test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Copyright 2015, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
var expect = require('expect');

var React = require('react/addons');
var I18NStore = require('../../../stores/I18NStore');
var I18NActions = require('../../../actions/I18NActions');
var LangSelector = require('../LangSelector');

describe('LangSelector', () => {
afterEach((done) => {
React.unmountComponentAtNode(document.body);
document.body.innerHTML = '';
setTimeout(done);
});

it('checks default', () => {
var lbl;
var value;

const cmp = React.render(<LangSelector/>, document.body);
expect(cmp).toExist();

const cmpDom = React.findDOMNode(cmp);
expect(cmpDom).toExist();
expect(cmpDom.id).toNotExist();

const select = cmpDom.getElementsByTagName("select").item(0);
const opts = select.childNodes;
const langs = I18NStore.getSupportedLocales();

for (let i = 0; i < opts.length; i++) {
lbl = opts[i].innerHTML;
value = opts[i].value;
expect(langs.hasOwnProperty(lbl)).toBe(true);
expect(langs[lbl]).toBe(value);
}
});

it('checks if a change of the compo fires the proper action', () => {
const spy = expect.spyOn(I18NActions, 'launch');

const cmp = React.render(<LangSelector/>, document.body);
const cmpDom = React.findDOMNode(cmp);
const select = cmpDom.getElementsByTagName("select").item(0);

select.value = "it-IT";
React.addons.TestUtils.Simulate.change(select, {target: {value: 'it-IT'}});
// select.children[1].click();

expect(spy.calls.length).toBe(1);
expect(spy.calls[0].arguments).toEqual([I18NActions.CHANGE_LANG, {locale: "it-IT"}]);
});
});
10 changes: 10 additions & 0 deletions web/client/dispatchers/Dispatcher.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Copyright 2015, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
var Dispatcher = require('flux').Dispatcher;

module.exports = new Dispatcher();
37 changes: 12 additions & 25 deletions web/client/examples/viewer/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
var React = require('react');
var MapViewController = require('../../components/Map/MapViewController');
var InfoButton = require('../../components/InfoButton/InfoButton');
var I18N = require('../../components/I18N/I18N');
var LangSelector = require('../../components/LangSelector/LangSelector');
var ConfigUtils = require('../../utils/ConfigUtils');
var url = require('url');

Expand All @@ -22,44 +24,29 @@ Api.getMergedConfig("../data/mapStoreConfig.json", url.parse(window.location.hre
});

React.render(<InfoButton
text="About"
title="About this app..."
text={<I18N.Message msgId="aboutLbl"/>}
title={<I18N.Message msgId="about_title"/>}
glyphicon="info-sign"
body={
<div>
<h1>MapStore 2</h1>
<p>
MapStore 2 is a framework to build web mapping applications using
standard mapping libraries, such as <a href="http://openlayers.org/">OpenLayers 3</a> and <a href="http://leafletjs.com/">Leaflet</a>.
<I18N.Message msgId="about_p0-0"/> <a href="http://openlayers.org/">OpenLayers 3</a> <I18N.Message msgId="about_p0-1"/> <a href="http://leafletjs.com/">Leaflet</a>.
</p>
<p>MapStore 2 has several example applications:</p>
<p><I18N.Message msgId="about_p1"/></p>
<ul>
<li>
MapViewer is a simple viewer of preconfigured maps (optionally
stored in a database using GeoStore)
<I18N.Message msgId="about_ul0_li0"/>
</li>
<li>
MapPublisher has been developed to create, save and share in a
simple and intuitive way maps and mashups created selecting
contents by server like OpenStreetMap, Google Maps, MapQuest or
specific servers provided by your organization or third party.
For more information check the <a href="https://github.com/geosolutions-it/MapStore2/wiki">MapStore wiki</a>.
<I18N.Message msgId="about_ul0_li1"/> <a href="https://github.com/geosolutions-it/MapStore2/wiki">MapStore wiki</a>.
</li>
</ul>
<h2>License</h2>
<h2><I18N.Message msgId="about_h20"/></h2>
<p>
MapStore 2 is Free and Open Source software, it is based on
OpenLayers 3, Leaflet and ReactJS, and is licensed under the
Simplified BSD License.
<I18N.Message msgId="about_p3"/>
</p>
<h2>Contributing</h2>
<p>We welcome contributions in any form:</p>
<ul>
<li>pull requests for new features</li>
<li>pull requests for bug fixes</li>
<li>pull requests for documentation</li>
<li>funding for any combination of the above</li>
</ul>
<p>For more information check <a href="https://github.com/geosolutions-it/MapStore2/blob/master/CONTRIBUTING.md">this</a> page.</p>
<p><I18N.Message msgId="about_p5-0"/> <a href="https://github.com/geosolutions-it/MapStore2/blob/master/CONTRIBUTING.md"><I18N.Message msgId="about_a0"/></a> <I18N.Message msgId="about_p5-1"/></p>
</div>
}/>, document.getElementById("aboutContainer"));
React.render(<LangSelector/>, document.getElementById("langSelContainer"));
8 changes: 8 additions & 0 deletions web/client/examples/viewer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,19 @@
position: absolute;
margin: 8px;
}
#langSelContainer {
z-index: 10;
bottom: 0px;
left: 0px;
position: absolute;
margin: 8px;
}
</style>
</head>
<body>
<div id="mapContainer"></div>
<div id="aboutContainer"></div>
<div id="langSelContainer"></div>
<script src="../../dist/mapstore-commons.js"></script>
<script src="../../dist/viewer.js"></script>
</body>
Expand Down
Loading

0 comments on commit a2ce0bf

Please sign in to comment.